New features for Python 3.10 is now on beta, this means that no new features will be added for this version.
Let’s take a look to the most important features this version will onboard.
Structural Pattern Matching
This one is a big one, evaluating objects can be tedious and not pretty. This is where Structural Pattern comes into play.
Before, we had to write stuff like:
if 'key' in my_map:
do_stuff
elif 'value' == my_map['this_key']:
do_stuff
elif my_map['code'] in [400,403]:
do_stuff
else:
do_other_stuff
Now, we can relay on Structural Patterns:
match status:
case 401 | 403:
return 'Unauthorized'
case 200:
return 'OK'
case _:
return 'Unexpected
Note that case _:
if the default for this syntax, also unlike in C/C++ with switch/case when you meet the next case
statement, the execution will got outside of the match
statement.
This is the structural part, now the pattern part:
match status:
case {"status": 'error', "err_code": err, **rest}:
return f"Error:{err} : {rest}"
case {"status": 'success', **rest}:
return f"Success: {rest}"
case _:
return "Unexpected"
On this example, if status have 'status' == 'error'
and an 'err_code'
key, it will fall to the first case. When 'status' == 'success'
it falls to the second case.
Whenever status isn’t in any of the previous case (note that this include status is an object with no get() method) it will fall on the default case.
Note that we can assign a variable to parts of the expression we are evaluating. This is basically syntax sugar, but that will allow a nice code readability. I’m really impatient to be able to use this new functionality in AWS Lambda development.
The code I showed uses int of dict, but this feature can be used with many other types, and this is just a quick overview of what can be achieved.
Type Unions
Until now, to specify that a parameter can be any of multiple types to the typing hints, we had to import typing.Union and use a syntax like
def my_function(param: typing.Union[int, str]):
pass
To achieve the exact same thing, python 3.10 allows:
def my_function(param: int | str):
pass
This syntax offers 2 massive advantages:
- It’s easier to read
- It avoid importing typing.Union, this mean cleaner import statements
Type Alias
This feature, will allow to prevent using my_var = 'MyType'
as placeholder for a type that is not yet declared. Instead now it’s possible to use my_var: TypeAlias = 'MyType'
. The difference is quite minimal, but this allows to know the string is a placeholder and not a literal string.
Type Guards
To avoid type errors, it’s common to have the necessity of testing the type deeper than a simple use of isinstance()
, like checking if all element of a list had the same type, or if a map has certain elements.
Type guards are typing int to check this. It’s used like this:
def is_str_list(val: List[Object] -> TypeGuard[List[str]]:
return all(isinstance(x, str) for x in val)
This function will check if all elements of a list are strings, using TypeGuard
instead of bool
, will allow us to directly know what type check is done by the function.
TypeGuard is in typing library