Beware! A new Python is approaching!

Overview

Structural Pattern Matching

Pattern Matching example 1

def analyze_input_from_user():
text = input("Enter some text: ")
match text.split():
case []:
print("Got nothing")
case [("add" | "subtract") as cmd, a, b]:
print(f"Got operation {cmd} with parameters {a} and {b}")
case [x] if x.isdigit():
number = int(x)
print(f"Got a single integer number: {number}")
case [x] if re.match(r"^[+-]?\\d+(.\\d*)?$", x):
number = float(x)
print(f"Got a single floating point number: {number:.3f}")
case [word]: # Must be non-numeric, since numerics are matched above
print(f"Got a single non-numeric word: {word}")
case _:
print("Got something else")
def analyze_input_from_user_the_old_way():
text = input("Enter some text: ")
words = text.split()
if len(words) == 0:
print("Got nothing")
elif len(words) == 3 and words[0] in ["add", "subtract"]:
(cmd, a, b) = words
print(f"Got operation {cmd} with parameters {a} and {b}")
elif len(words) == 1 and words[0].isdigit():
number = int(words[0])
print(f"Got a single integer number: {number}")
elif len(words) == 1 and re.match(r"^[+-]?\\d+(.\\d*)?$", words[0]):
number = float(words[0])
print(f"Got a single floating point number: {number:.3f}")
elif len(words) == 1:
word = words[0]
print(f"Got a single non-numeric word: {word}")
else:
print("Got something else")

Pattern matching example 2

issues = [
{
'issuer': "Mr Praline",
'date': date(1969, 12, 7),
'type': 'complaint',
'title': "a Dead Parrot",
},
{
'issuer': "A man",
'date': date(1972, 11, 2),
'type': 'request',
'subject': "an argument",
'location': "the Argument Clinic",
},
"Now something completely different",
]
def print_info_about_issues():
for issue in issues:
print_info_about_issue(issue)
def print_info_about_issue(issue):
match issue:
case {'type': 'complaint', 'date': d, 'title': t, 'issuer': i}:
print(f"{i} complained about {t} on {d}.")
case {
'type': 'request',
'date': date(year=y),
'subject': s,
'location': l,
'issuer': i,
}:
print(f"{i} requested to have {s} on {y} at {l}.")
case str(text):
print(f"Textual issue: {text}")
def print_info_about_issue_the_old_way(issue):
if isinstance(issue, dict) and (
issue.get('type') == 'complaint' and
'date' in issue and 'title' in issue and 'issuer' in issue):
d = issue['date']
t = issue['title']
i = issue['issuer']
print(f"{i} complained about {t} on {d}.")
elif isinstance(issue, dict) and (
issue.get('type') == 'request' and
isinstance(issue.get('date'), date) and
'subject' in issue and 'location' in issue and 'issuer' in issue):
y = issue['date'].year
s = issue['subject']
l = issue['location']
i = issue['issuer']
print(f"{i} requested to have {s} on {y} at {l}.")
elif isinstance(issue, str):
print(f"Textual issue: {issue}")

Tool support

More pattern matching

Typing related improvements

Writing Union types as X | Y

from decimal import Decimaldef format_euros(value: float | Decimal | None) -> str | None:
if value is None:
return None
return '{:.2f} €'.format(value)
from decimal import Decimal
from typing import Optional, Union
def format_euros_old(value: Optional[Union[float, Decimal]]) -> Optional[str]:
if value is None:
return None
return '{:.2f} €'.format(value)
assert isinstance(3.5, float | None)
assert issubclass(bool, int | str) # since bool is subclass of int
assert not isinstance("3.10", float | int | bytes)

Parameter Specification Variables

from typing import Callable, ParamSpec, TypeVarP = ParamSpec("P")
R = TypeVar("R")
measured_times: dict[str, float] = {}def time_measured(f: Callable[P, R]) -> Callable[P, R]:
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
t0 = time.perf_counter()
result = f(*args, **kwargs)
t1 = time.perf_counter()
measured_times[f.__name__] = t1 - t0
return result
return inner
@time_measured
def repeated_string(a: str, n: int) -> str:
return a * n
repeated_string("ABC", 123) # This is OK
repeated_string(123, "ABC") # This should be rejected by type checker

Explicit Type Aliases

TreeMap = "dict[str, Tree]"class Tree:
def __init__(self, subtrees: TreeMap) -> None:
self.subtrees = subtrees
from typing_extensions import TypeAliasTreeMap: TypeAlias = "dict[str, Tree]"class Tree:
def __init__(self, subtrees: TreeMap) -> None:
self.subtrees = subtrees

Postponed evaluation of annotations

Should I already update?

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store