Learn to Code by Solving Problems: A Python Programming Primer Homepage for the book Learn to Code by Solving Problems: A Python Programming Primer

What Beginning Coders Might Care About in Python 3.10

Hello, Python people,

My Learn to Code by Solving Problems book uses Python to teach you how to program. About once a year, a new Python version is released with new features and improvements. For the beginning programmer (or most programmers, really), there likely won’t be anything earth-shattering: you can continue to program as you have before. But why not take a look at each Python release to see if there’s anything you like? I almost always find something that I can incorporate into my Python coding.

I’ve done this here for Python 3.10, from the perspective of the beginning programmer. At the time of writing, Python 3.10 is on beta 4. It’s generally not advised to work with beta versions of Python for serious work, but do read on if you’re curious about what’s coming down the pike.

Note: the version of Python before Python 3.10 is Python 3.9. The book assumes that you’re using at least Python 3.6, and everything will work as expected in later versions of Python as well, including Python 3.10.

Better Error Messages

Error messages can be tricky for new learners, especially if written in ways that make them difficult to interpret. Python 3.10 makes improvements to error messages, which I find very valuable for teaching and learning purposes.

Here’s some Python code with a syntax error:

x = 'this is a very long string with a missing closing quote

for i in range(5):
    print(i)

Here’s what Python 3.9 gives us:

    x = 'this is a very long string with a missing closing quote
                                                                ^
SyntaxError: EOL while scanning string literal

What’s EOL? That’s an example of something that can be confusing to new learners. And what does ‘scanning’ mean?

Here’s the better error message from Python 3.10:

    x = 'this is a very long string with a missing closing quote
        ^
SyntaxError: unterminated string literal (detected at line 1)

To me, this is much more clear, and avoids unnecessary jargon.

Here’s another example. Say you forget the colon on the first line of a for loop, like this:

>>> for x in range(5)

Oops, a missing colon. We’ve all done that.

The error that Python 3.9 gives us isn’t very specific. It tells us that we made a syntax error, but that’s it:

  File "<stdin>", line 1
    for x in range(5)
                     ^
SyntaxError: invalid syntax

By contrast, here’s what Python 3.10 more helpfully says:

  File "<stdin>", line 1
    for x in range(5)
                     ^
SyntaxError: expected ':'

It tells us exactly which character we should add!

Here’s one more. Using Python 3.9, try to spot the error:

>>> x = 5
>>> if x = 5:
  File "<stdin>", line 1
    if x = 5:
         ^
SyntaxError: invalid syntax

It’s the classic error of using one equals sign instead of two. Oops! With Python 3.10, we are explicitly told about this mistake:

>>> x = 5
>>> if x = 5:
  File "<stdin>", line 1
    if x = 5:
       ^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

There are improvements to error messages about indentation errors, attribute errors, name errors… all of our favourites. :) For the full list of improved error messages, check out the Python 3.10 Release Notes under Better error messages. Not all of them may make sense to you right now, but you can be sure that these improvements will be there waiting to help you along when you start exploring new Python features.

New Match Statement

Almost from the beginning of the book (Chapter 2!), we’ve used if statements to make decisions in our code. Python 3.10 adds a new way to make decisions using the new match statement. I don’t think I’d use this for any of the if-statement code in the book, but sometimes match can make your code more concise. (If you’ve used a language like C or Java with a switch statement, then you have an idea what’s coming – but Python’s match is even more powerful!)

How it works is that we indicate what we want to match – the Python documentation calls this the subject. For example, the subject could be an integer or a list. Then, we indicate the patterns that we care about matching, and write code that Python will execute when a pattern matches.

In Chapter 2, we solved the Telemarketers Problem. (Read that chapter for free here!.) We used an if statement to solve it. Here, we’ll use the new match statement. I encourage you to go back and read the problem description to refresh yourself on what we have to do.

First of all, let’s just see if we can detect when a number is an 8 or a 9. This will be a useful stepping stone for the full problem. Here’s how it looks using match:

num = int(input())

match num:
    case 8:
        print('Got one!')
    case 9:
        print('Got one!')
    case _:
        print('No!')

We have three patterns here, each following the case keyword: the first matches 8, the second matches 9, and the third matches anything else. As with an if…elif…elif…else chain, Python checks the patterns from top to bottom, running the first one that matches. If a pattern matches, then no later pattern is tried. The _ wildcard pattern at the bottom matches any value not matched by any of the above patterns.

Hmm – there’s some code duplication here, because the 8 and 9 patterns print the same thing (we’re using the same print call in each case). Let’s use Python’s nifty “or pattern” to condense things:

num = int(input())

match num:
    case 8 | 9:
        print('Got one!')
    case _:
        print('No!')

Alright, good! Now let’s see if we can take in two numbers and determine whether each one is an 8 or a 9. Here goes:

num1 = int(input())
num4 = int(input())

match [num1, num4]:
    case [8 | 9, 8 | 9]:
        print('Got them!')
    case _:
        print('No!')

This time, our subject is [num1, num4]; that is, a list of two integers. To capture the condition that each number is an 8 or a 9, we need to combine two or patterns in a list as I have done here. Be sure to try out this program before continuing and make sure that only combinations of 8s and 9s match the pattern.

Now we’re ready to solve the full Telemarketers problem using match. Here we go!

1
2
3
4
5
6
7
8
9
10
num1 = int(input())
num2 = int(input())
num3 = int(input())
num4 = int(input())

match [num1, num2, num3, num4]:
    case [8 | 9, num2, num3, 8 | 9] if num2 == num3:
        print('ignore')
    case _:
        print('answer')

This time, our subject is four values, not two or one (6). In addition to checking that both num1 and num4 are 8 or 9, we need to check that num2 and num3 are equal. I do this using a condition following the keyword if (7). If the condition if num2 == num3 is true, then this full pattern matches; otherwise, it doesn’t.

I think for this example that the if-based code in the book is clearer than the match-based code I presented here. But your mileage may vary! In any case, the match statement is just a new tool that you can use when appropriate. It certainly doesn’t replace the if statement but rather complements it in certain situations. If you find examples or exercises in the book where you prefer match to an if statement, please do let me know!

The true power of match is when it’s used to pull apart the structure of data. For example, suppose that we were looking for a sequence whose first value can be anything, whose second value must be a sequence whose first value is 6, and whose other values (if any) can be anything. We can concisely encode this using match as follows:

structure = [5, [6, 7], [[8]]]

match structure:
    case [_, [6, *_], *_]:
        print('Correct structure')
    case _:
        print('No!')

An underscore _ in the pattern means that, to match it, we need a value there but that we don’t need a name with which to refer to it. The *_ syntax means that we need zero or more values.

If you’d like to learn more about the match statement, I recommend the Structural Pattern Matching Tutorial.

Wrapping Up

There’s a lot of other new stuff in Python 3.10, but the above is what I think will apply to most Python programmers, including those just getting started or working through the book.

A heartfelt thank-you to everyone who works tirelessly to push the boundary and make Python better and better. For a high-level language great for learning from scratch or making the next killer app, I think Python is unmatched.