Hello, buddies! What sets Python apart is its intuitive features like libraries, high productivity, ease of learning, etc. According to a recent study, almost 27% of the advertised jobs require Python as a core skill — up from 18.5% at the beginning of the year😮😮
Last October, Python released version 3.9. The updates included improvements in Python internals, performance boosts, dictionary union operators, handy new string functions, new type operations, consistent and stable internal APIs, and more.
So on this 4th of October, Python is going to release 3.10, which has awesome features 👏👏
Better Error Messages
In general, Python error messages can be misleading and confusing. That is why this feature is here to help us. This feels like an important topic that is going to make our day-to-day job a lot easier and I am really looking forward to it.
So first let's take a look at how Python 3.9 gives error messages.
There are several types of errors where the current Python incarnation doesn’t provide enough information. For example, observe SytaxError for the following code:
var newArticle = new Article
{
Title = "C# 10 - Top 5 new features",
Category = ".NET",
ReleaseDate = DateTime.Now()
}
File "<ipython-input-5-e9aa07ca09db>", line 2
processed_data = process_data(data)
^
As you can see, the square bracket is missing at the end of the first line. However, SyntaxError reports a problem in the second line. A similar situation can happen if we forget to close string quotes:
word = "BirdIsThe
processed_data = process_data(data)
ile "<ipython-input-6-44253c1ee7d8>", line 1
word = "BirdIsThe
^
SyntaxError: EOL while scanning string literal
SyntaxError: invalid syntax
In this case, it can be even more misleading because EOL or EOF is reported 😑😑
Now let's see how Python 3.10 is going to treat this.
There are several areas where we will get better messages from Python 3.10. Let’s start with the problems displayed in the chapter above. If you forget to close the bracket like in the example above you will get this:
data = [2, 9, 11, 33, 56, 93, 111,
^
SyntaxError: '[' was never closed
Or if you forget to close the string:
word = "BirdIsThe
^
SyntaxError: unterminated string literal (detected at line 1)
Or if you messed up your generator expression:
some_function(x, y for y in range(11))
^^^^^^^^^^^^^^^^^^^^
SyntaxError: Generator expression must be parenthesized
All these messages are more specific and concrete. All the Pythonistas going to love it 😍😍
However, that is not all. Python 3.10 improved other messages as well. If you forget to put two dots after a statement, now you will know that more explicitly:
if (2 > 9)
if (2 > 9)
^
SyntaxError: expected ':'
Or if you used =
insted of ==
in the mentioned statement:
if (2 = 9):
pass
if (2 = 9)
^
SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='?
Wow, error messages are clear as in Unity 🥰🥰
Also, if you forget comma in the collection, Python 3.10 has got your back:
list = [2, 9, 11, 33 56, 93]
File "<stdin>", line 1
list = [2, 9, 11, 33 56, 93]
Talking about collections, if you forgot to assign value to a dictionary key, Python 3.10 will let you know:
dict = { 'a' : 2, 'b' : 9, 'c' : , 'd' : 33 }
File "<stdin>", line 1
dict = { 'a' : 2, 'b' : 9, 'c' : , 'd' : 33 }
^
SyntaxError: expression expected after dictionary key and ':'
There are other improved messages as well, however, I found these the most useful, especially if you are a beginner.
Structural Pattern Matching
This feature is definitely going to have the biggest impact. If you are familiar with a switch-case
statement from any other language, this one is that, but on steroids. Wait, there was not switch-case mechanism in Python until now? Why is it called pattern matching? What is the difference? Let’s see.
Yes, Python was not supporting switch-case statements, or simillar concepts until now. Weird, I know. There were several attempts to include this concept in the language, however, they were always rejected. Nobody has been able to suggest an implementation that can be integrated properly with Python’s syntax and the overall spirit of the language. This means that if you wanted to check multiple values, you need to do something like this:
def http_error(status):
if 400:
return "Bad request"
elif 404:
return "Not found"
elif 418:
return "I am not joking"
Or something like this:
def http_error(status):
return_value = {
400: "Bad request",
404: "Not found",
418: "I am not joking" }
return return_value[status]
To be honest, I was OK with it. In general, I am not a big fan of the switch-case statement in other languages as well. It usually means that architecture could be done better or that you could use concepts like polymorphism and dictionaries.
That being said, there are certain situations in which a switch-case statement is useful. So Python 3.10 gonna treat it 👏👏
The easiest way to use this Python feature is to compare a variable with several values. There are two new keywords match and case. The example from above then can be written down like this:
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I am not joking"
case _:
return "Well, I am joking"
So, you know, a regular switch-case statement. The last one case _
is the default value – wildcard, so if the status has a value that is not 400
, 404
, or 418
, it will return “Well, I am joking” as a result. This is optional, so if you don’t include it and some out-of-the-scope value appears, the behavior is a no-op. This wildcard is very powerful and can help us with more complex pattern matching, as we will see in a bit.
You can also chain values if you want to do the same thing for a certain case. For example:
def ide_full_name(short_name):
match short_name:
case "PyC":
return "PyCharm"
case "VSC":
return "Visual Studio Code"
case "VS2017" | "VS2019" | "VS2022":
return "Visual Studio"
case _:
return "Not Supported"
Cool, right! This is going to be the biggest change in 3.10. And I bet most of you don't know Python was not supporting switch-case statements 😝😝
New Type Union Operator
This is a new feature that will simplify the code. Every Python release comes with a set of type hints features. From our perspective, this one is the most important of such features.
With the current Python version, if you want to use type hints for function that has a parameter that can receive values of different types, you had to use Union type. Something like this:
def some_funcion(flexible_parameter: Union[int, string]) -> Union[int, string]:
return flexible_parameter
Therefore, Python 3.10 introduces new union operand – |
. What this operand says is that certain parameters can be either Type 1
either Type 2
. Now, the previous function can be written like this:
def some_funcion(flexible_parameter: int | string) -> int | string:
return flexible_parameter
Precise Line Numbers for Debugging
Have you noticed that sometimes the debugger doesn’t show the real number of the line where the code has a mistake? It does work well most of the time, but it was not always the most reliable tool 🙄🙄
If you use sys.settrace
and associated tools you may notice that tracing doesn’t work 100% of the time. Sometimes the line is not correct. Reliability here is not as you would expect from such a mature programming language. The problem in its essence is that the f_lineno
attribute of frame objects that are part of event objects should always contain the expected line number
So Python 3.10, brings more precise and reliable line numbers for debugging, profiling, and coverage tools. Python 3.10 guarantees that tracing events, with the associated and correct line numbers, are generated for all lines of code executed and only for lines of code that are executed.
In order to do this, Python 3.10 doesn’t rely on the current form of co_lnotab attribute of the events. This version of Python uses new method co_lines()
, which returns an iterator over bytecode offsets and source code lines. The change is done in a way that the f_lineno
attribute of frame objects will always contain the expected line number.
And some other language changes
The int type has a new method int.bit_count(), returning the number of ones in the binary expansion of a given integer, also known as the population count.
The views returned by
dict.keys()
,dict.values()
anddict.items()
now all have a mapping attribute that gives atypes.MappingProxyType
object wrapping the original dictionary.- The
zip()
function now has an optional strict flag, used to require that all the iterables have an equal length. - Assignment expressions can now be used unparenthesized within set literals and set comprehensions, as well as in sequence indexes (but not slices).
There are many other but I found these are the useful ones.
So, buddies, these are the major and useful features that make Python more beautiful! For me, error messages are the best things. Happy Coding!