Cursus
Python won de TIOBE "Language of the Year"-award drie keer in de afgelopen vier jaar. De populaire programmeertaal staat halverwege 2022 nog steeds bovenaan de TIOBE-index.

Python staat bovenaan de TIOBE-index van juni 2022.
Dat Python een score van 12,74% heeft, betekent dat het trefwoord “Python” opdook in ongeveer ~13% van alle programmeergerelateerde zoekopdrachten. Met andere woorden: elke 7e of 8e programmeervraag op internet gaat over Python.
Vandaag de dag wordt Python vaak geassocieerd met de grootste doorbraken in data science en AI. Gebruikers waarderen de eenvoud, de grote set ingebouwde en externe libraries voor talloze taken en de community.
Met de definitieve stabiele versie van 3.11 op 3 oktober 2022 kan de Python-community uitkijken naar allerlei welkome updates, waaronder een aanzienlijke snelheidswinst, betere foutafhandeling en debugging, en tal van quality-of-life-verbeteringen aan standaardbibliotheken.
Wat zijn de nieuwe features van Python 3.11?
De Python 3.11 changelog bestaat uit een schier eindeloze lijst met bugfixes, verbeteringen en toevoegingen, waarvan je de meeste waarschijnlijk nooit zult opmerken. Toch zijn er een paar cruciale nieuwe features die je Python-werkflow aanzienlijk kunnen verbeteren zodra de stabiele release er is. Deze nieuwe features staan hieronder beschreven.
1. Snelheidsverbetering
De eerste grote verandering waar data scientists enthousiast van worden, is de snelheidsverbetering—de standaard benchmark-suite draait ongeveer 25% sneller vergeleken met 3.10. De Python-documentatie stelt dat 3.11 in sommige gevallen tot 60% sneller kan zijn. Zo voer je zelf de benchmarktest uit om de snelheidswinst te testen.
Om de snelheden van Python 3.10 en 3.11 te vergelijken, heb je een Docker-installatie nodig. Zorg dat Docker Desktop draait en voer vervolgens deze twee commando’s uit in de terminal; hiermee download je twee images voor de twee Python-versies.
$ docker run -d python:3.10.4-bullseye
$ docker run -d python:3.11-rc-bullseye
De gedownloade images zijn zichtbaar in je Docker-dashboard, waar je ze ook kunt starten.

Nadat je de containers hebt gestart, kun je docker ps uitvoeren om details te zien, zoals container-ID, de image, de status en meer.

De draaiende containers zijn ook zichtbaar onder het tabblad Containers in je dashboard, waar je ook CLI-knoppen naast "Run/Stop" vindt. Klik erop voor beide containers om twee terminaltabs te openen die aan de containers zijn gekoppeld.
Installeer in beide tabs het pakket pyperformance via pip. Voer vervolgens onderstaande commando’s uit in elke containertab.
Voor Python 3.10
$ pyperformance run -r -o py310.json
Voor Python 3.11
$ pyperformance run -r -o py310.jsone run -r -o py311.json
Deze commando’s draaien benchmarktests voor zo’n 60 Python-functies uit standaardbibliotheken en produceren JSON-metadata. Ik heb optioneel de tag -r toegevoegd om pyperformance te laten weten dat ik rigoureuze tests wil, wat meer tijd kost. In mijn geval draaien beide containers op een Windows-machine met een AMD Ryzen 12-core CPU.
We hebben nu twee JSON-bestanden in twee containers en we moeten ze in één map plaatsen om de snelheidstest voort te zetten. Daarvoor gebruiken we het commando docker cp om het JSON-bestand van de Python 3.11-container naar 3.10 te kopiëren.
Voer onderstaande commando’s uit in een aparte terminaltab en pas het pad aan naar het juiste pad op jouw computer:
$ docker cp crazy_snyder:/py311.json C:/Users/bex/Desktop
$ docker cp C:/Users/bex/Desktop/py311.json charming_proskuriakova:/
In mijn geval zijn crazy_snyder en charming_proskuriakova de ID’s van de draaiende containers. Nu staan beide JSON-bestanden in de Python 3.10-container. Om de benchmark af te ronden en de resultaten als CSV op te slaan, voer je onderstaande commando’s uit.
$ pyperformance compare py310.json py311.json --csv comparison.csv
$ docker cp charming_proskuriakova:/comparison.csv C:/Users/bex/Desktop/articles
Tot slot laden we de CSV in Pandas en analyseren we deze:
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
df = pd.read_csv("data/comparison.csv")
df.rename({"Base": "py310", "Changed": "py311", "Benchmark": "function"},
axis=1,
inplace=True)
>>> df.shape
(59, 3)
>>> df.sample(5)
| Function | py310 | py311 | |
| 40 | scimark_monte_carlo |
0.12392 | 0.08052 |
| 19 | mako |
0.01967 | 0.01289 |
| 1 | chameleon |
0.01131 | 0.00843 |
| 8 | float |
0.14265 | 0.09133 |
| 35 | regex_effbot |
0.00315 | 0.00261 |
Elke rij in de dataframe registreert hoeveel tijd (in milliseconden) beide versies nodig hadden om de gegeven Python-functie uit te voeren. Laten we een histogram van speedups plotten.
df["speed_up"] = (df["py310"] / df["py311"]).round(3)
fig, ax = plt.subplots()
sns.histplot(df["speed_up"], bins=11)
ax.set(xlabel="Speedup (times)",
title="Python 3.10 vs. Python 3.11 - speedup comparison")
plt.show()

Uit de plot blijkt duidelijk dat het merendeel van de functies in Python 3.11 ongeveer 1,2 keer (20%) sneller was. We kunnen de schatting ook bevestigen met het gemiddelde en de mediaan.
>>> df["speed_up"].mean()
1.2605593220338982
>>> df["speed_up"].median()
1.207
Er lijkt één geval te zijn waarin de snelheidswinst meer dan 60% bedroeg.
>>> df["speed_up"].max()
1.899
Er zijn ook vier gevallen waarin Python 3.10 sneller was dan de nieuwe versie.
>>> (df["speed_up"] <= 1).sum()
4
Hoewel de prestatieverbeteringen op papier duidelijk zijn, zal de specifieke impact per persoon verschillen en moet de stabiele, definitieve versie van Python 3.11 nog verschijnen. De benchmarktest gebruikt pure Python-functies, die je in je dagelijkse werk misschien niet veel gebruikt, maar een Docker-installatie voor zowel Python 3.10 als 3.11 opzetten geeft je wel een idee van de snelheidswinst voor jouw specifieke use-cases.
2. Betere foutmeldingen
Nog een interessante feature van Python 3.11 is betere foutmelding die exact aangeeft waar de fout zit. In plaats van een traceback van 100 regels die eindigt met een lastig te interpreteren foutmelding, wijst Python 3.11 naar de exacte expressie die de fout veroorzaakte.

In het bovenstaande voorbeeld wijst de Python-interpreter naar de x die het script liet crashen vanwege de waarde None. De fout zou in huidige Python-versies ambigu zijn geweest, omdat er twee objecten zijn met het attribuut ‘x’. De foutafhandeling in 3.11 wijst echter duidelijk de problematische expressie aan.
Dit voorbeeld laat zien hoe 3.11 een fout aanwijst vanuit een diep genest woordenboek en duidelijk kan tonen bij welke sleutel de fout hoort.

Je ziet dezelfde precisie in complexe rekenkundige expressies.

De vernieuwde foutmeldingen zijn absoluut een welkome toevoeging, zeker voor data scientists die worstelen met het debuggen van scripts.
3. Exception-notities
“Expliciet is beter dan impliciet.”
Bovenstaande zin is de tweede regel van the Zen of Python, een lijst met 20 ontwerpprincipes van Python. Deze regel staat voor de gedachte dat Python-code zo expressief mogelijk moet zijn.
Om dit ontwerppatroon te versterken introduceert Python 3.11 exception-notities (PEP 678). Binnen je except-clauses kun je nu de functie add_note() aanroepen en een aangepaste boodschap meegeven wanneer je een fout raised.
import math
try:
math.sqrt(-1)
except ValueError as e:
e.add_note("Negative value passed! Please try again.")
raise

Als je een aangepaste exception-klasse zoals hieronder hebt geschreven, kun je meerdere notities toevoegen aan de klasse binnen het beschermde class-level attribuut __notes__:
import math
class MyOwnError(Exception):
# Should be a list of strings
__notes__ = ["This is a custom error!"]
try:
math.sqrt(-1)
except:
raise MyOwnError

Wanneer de interpreter nu de fout tegenkomt, toont hij de aangepaste boodschap na de reguliere berichttekst.
Er is ook een nieuwe syntax om exceptions te raisen met de except*-clause, die samen met de nieuwe klasse ExceptionGroup wordt gebruikt. PEP 654 zegt dat je hiermee meerdere niet-gerelateerde fouten tegelijk kunt raisen in verschillende scenario’s. De meeste van deze situaties zijn nogal niche, zoals werken met web sockets, gelijktijdig programmeren met asyncio, enzovoort—zaken die zelden voorkomen in alledaags datawerk—maar als je geïnteresseerd bent, kun je hier meer leren over de nieuwe syntax.
4. Nieuwe typing-feature: Self
Statisch getypeerde talen helpen je code leesbaar en makkelijk te debuggen te maken. Het exact definiëren van het type van variabelen, functie-in- en -output kan je uren debugtijd besparen en maakt je code voor anderen beter leesbaar. Typing-annotaties zorgen er ook voor dat moderne IDE’s functiedefinities tonen terwijl je de namen typt, waardoor je functies voor anderen makkelijker te interpreteren zijn.
Tot nu toe had de krachtige typing-module in Python klassen voor vrijwel elk datatype, behalve voor klassen die instanties van zichzelf retourneren. In huidige Python-versies is onderstaand voorbeeld niet mogelijk:
from typing import Self
class Language:
def __init__(self, name, version, release_date):
self.name = name
self.version = version
self.release_date = release_date
def change_version(self, version) -> Self:
self.version = version
return Language(self.name, self.version, self.release_date)

Python 3.11 introduceert echter de klasse Self, die je kunt toevoegen aan de definitie van een functie als de returnwaarde self is of een nieuwe instantie van de klasse zelf.
5. Verbeteringen in de standaardbibliotheken
Er zijn nog een paar andere quality-of-life-verbeteringen aan de standaardbibliotheken. Allereerst zijn er twee langverwachte functies toegevoegd aan de math-module.
>>> import math
>>> math.cbrt(9) # Find the cube-root of x
2.080083823051904
>>> math.cbrt(27)
3.0000000000000004
>>> math.exp2(5) # Raise 2 to the power of x
32.0
Dat het 28 jaar duurde voordat Python de derdemachtswortel-functie toevoegde, is best verrassend, maar beter laat dan nooit.
De fractions-module kreeg ook een nieuwe feature waarmee je breuken kunt maken vanuit strings:
>>> from fractions import Fraction
>>> Fraction("22/7") # New in Python 3.11
Fraction(22, 7)
>>> Fraction(numerator=22, denominator=7)
Fraction(22, 7)
>>> Fraction("3e-4")
Fraction(3, 10000)
>>> Fraction("-73/41")
Fraction(-73, 41)
>>> Fraction(3.1415) # Find the closest approximation to the given float
Fraction(7074029114692207, 2251799813685248)
Zoals je ziet kan de fractions-module handig zijn voor bepaalde rekenkundige operaties. Ik vind vooral fijn dat je de dichtstbijzijnde breuk kunt vinden die een gegeven float benadert. Je kunt nog een stap verder gaan door een limiet voor de noemer op te geven:
>>> from math import pi
>>> Fraction(pi).limit_denominator(100000)
Fraction(312689, 99532)
Er is ook een nieuwe module genaamd tomllib voor het parsen van TOML-documenten. TOML (Tom's Obvious Minimal Language) is een populair bestandsformaat voor het schrijven van menselijk leesbare configuratiebestanden. Hier is een afbeelding van de projecthomepage:

import tomllib
toml_str = """
python-version = "3.11.0"
release_date = "2022-10-22"
"""
data = tomllib.loads(toml_str)
print(data) # {'python-version': '3.11.0', 'release_date': '2022-10-22'}
print(data['python-version']) # 3.11.0
print(data['release_date']) # 2022-10-22
Releasedatum Python 3.11
De ontwikkeling van Python 3.11 begon op 5 mei 2021. Sindsdien zijn er zeven alfa-versies uitgebracht, gevolgd door de eerste bèta op 8 mei 2022. Vanaf die datum tot aan de officiële release worden er geen nieuwe features meer aan Python toegevoegd.
De definitieve, stabiele release verschijnt op 3 oktober 2022, met tussendoor nog drie extra bèta’s en twee release-candidates. Wil je meer leren over Python, bekijk dan de volgende resources.
