Python 3.10 met structural pattern matching is verschenen

Het Python-releaseteam heeft versie 3.10.0 van de programmeertaal vrijgegeven. Python 3.10.0 biedt onder andere ondersteuning voor precise line numbering, waar tools voor debugging profijt van kunnen hebben, en structural pattern matching.

De Python Software Foundation zet de belangrijkste Python Enhancement Proposals of PEP's voor Python 3.10.0 op een rij. Daar staan onder andere de PEP's voor het voorbereiden op de verwijdering van het wstr-lid in PyUnicodeObject en het toestaan van writing union types zoals X | Y bij.

Daarnaast brengt PEP 626 precise line numbers voor debugging- en andere tools naar de 3.10-serie. Het releaseteam beschrijft dat Python hiermee moet garanderen dat line-tracingevents voor alle uitgevoerde en ook alleen voor uitgevoerde regels code gegenereerd moeten worden als tracing is ingeschakeld. Versie 3.10 voegt een co_lines-attribute toe voor debuggingtools. Die beschrijft de mapping van bytecode naar source.

Bij structural pattern matching wordt een patroon gematcht met een subjectwaarde. Die match kan succesvol zijn of niet. De techniek is geschikt om datastructuren uit elkaar te halen en maakt het daarmee mogelijk om cleanere, leesbaarder code te schrijven. Syntax voor patroonmatching zit al in onder andere de programmeertalen Haskell, Erlang en Ruby en voor JavaScript ligt er een voorstel voor ondersteuning. Bij Python was de ondersteuning tot nu toe beperkt en de functionaliteit in 3.10 bouwt voort op de gelimiteerde ondersteuning die er al was. De specificatie van en achterliggende motivatie voor structural pattern matching zijn verder beschreven in PEP 634, 635 en 636. De belangrijkste wijzigingen zijn verder die voor betere errorberichten en parenthesized context managers.

Python 3.10

Door Olaf van Miltenburg

Nieuwscoördinator

05-10-2021 • 09:59

61

Reacties (61)

61
61
26
6
0
17
Wijzig sortering
De belangrijkste wijzigingen zijn verder die voor betere errorberichten
Dit is echt een grote verbetering voor zowel beginners als gevorderde Python gebruikers. De errors geven nu veel beter aan wat er met je syntax mis is, vergelijkbaar met hoe dat gebeurt in Rust.

Bij de volgende foutieve syntax (er mist een closing curly bracket):
expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
38: 4, 39: 4, 45: 5, 46: 5, 47: 5, 48: 5, 49: 5, 54: 6,
some_other_code = foo()
Krijg je in plaats van:
File "example.py", line 3
some_other_code = foo()
^
SyntaxError: invalid syntax
de volgende error:
File "example.py", line 1
expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
^
SyntaxError: '{' was never closed
*Code block werkt niet, maar in de output hoort ^ onder de {

zie https://docs.python.org/3...tml#better-error-messages voor meer informatie.

[Reactie gewijzigd door Tk55 op 25 juli 2024 14:29]

Kun je dan niet beter in Rust beginnen?
Beginnen vast wel. Als je al begonnen bent, dan kan je ook gewoon doorgaan. Er zijn voor elke taal redenen om bij die taal te blijven en om over te stappen naar een andere taal.
Ligt er aan wat je wil bereiken. Ik heb wel eens de vergelijking gehoord dat programmeertalen net hamers zijn. Je pakt bijvoorbeeld geen paalhamer om een spijker in een muur te slaan, maar een kleiner hamertje waarmee je minder kracht hebt, maar wel sneller de spijker in de muur krijgt. En zo is dat met programmeertalen ook.
De talen hebben beide sterke punten.

Rust gecompileerde code is sneller en efficiënter. De code is erg gestructureerd, perfect voor systeemsoftware, embedded, algoritmes, belangrijke webservices et cetera.

Python heeft veel en hele krachtige libraries, zoals die voor data-analyse en AI. Het programmeert snel en er zijn veel ontwikkelaars voor te vinden. Het is perfect voor analyses, scripttaken, desktopapplicaties en webservices.
Por que no los dos?
Altijd leuk als in een artikel op Tweakers bepaalde termen cursief zijn om aan te geven dat het geen heel gangbare woorden zijn, om vervolgens 0 uitleg te geven.
En ja, ik kan het zelf wel opzoeken, maar zet ze dan niet cursief :+

Admin-edit:Bedankt voor je feedback. Commentaar voor de redactie hoort echter thuis in Geachte Redactie. Hier staat het de inhoudelijke discussie niet in de weg en kan de redactie het eenvoudig terugvinden.

[Reactie gewijzigd door Bor op 25 juli 2024 14:29]

Mee eens. @Olaf, als je "structural pattern matching" als grote vernieuwing in de titel zet, mag er ook wel een beetje uitleg bij in het artikel.
Ik was zelf ook wel erg benieuwd wat structural pattern matching nou precies is. Voor iedereen die geen zin heeft om door te klikken: het is een toevoeging die op het eerste gezicht veel lijkt op een klassieke 'switch' structuur, maar dan met een hele hoop mooie features:
match status:
case 400:
return "Bad request"
case 401 | 403 | 404:
return "Not allowed"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the Internet"
Ziet er erg krachtig uit. Ga ik zeker gebruiken als de ondersteuning wat breder is.


* Edit: erg onhandig als inspring in je bericht worden genegeerd als het om een taal als Python gaat.

[Reactie gewijzigd door lvdgraaff op 25 juli 2024 14:29]

Wat is hier anders aan dan switch statements? Als programmeur met ervaring in zowel C als Python ziet dit er precies hetzelfde uit.

[Reactie gewijzigd door youridv1 op 25 juli 2024 14:29]

Ik denk dat het juist zo briljant is dat het er precies als een klassieke switch uitziet. Totdat je dit soort code tegenkomt:
from dataclasses import dataclass

@dataclass
class Point:
x: int
y: int

match point:
case Point(x=0, y=0):
print("Origin")
case Point(x=0, y=y):
print(f"Y={y}")
case Point(x, y) if x == y:
print(f"Y=X at {x}")
case Point():
print("Somewhere else")
case _:
print("Not a point")
Hier worden de properties x en y van het Point object uitgepakt in het match statement, zodat je daar direct tegen kan matchen. Of er wordt alleen tegen één eigenschap gematcht. Of er wordt een voorwaarde aan verbonden met het if statement. Dat overstijgt de klassieke switch toch wel?

[Reactie gewijzigd door lvdgraaff op 25 juli 2024 14:29]

Wat in bijna elke moderne taal perfect op te lossen is met een switch true

switch(true){
;;case !(point instanceof Point):
;;;;;print('Not a point');
;;;;;break;
;;case point.x == 0 && point.y == 0:
;;;;;print('Origin')
;;;;;break;
;;case point.x == 0:
;;;;;print(point.y);
;;;;;break;
;;case point.x === point.y:
;;;;;print('X=Y at '+point.x);
;;;;;break;
;;default:
;;;;;print('Somewhere;else')
}

Imo is dit stuk leesbaarder als de de vage inline if en y=y van pyhton (wat toch claimed meest leesbare code op te leveren)

Really tweakers, geen code blocks? Dan maar inspringen met ; :+

[Reactie gewijzigd door jaenster op 25 juli 2024 14:29]

Of gewoon pattern matching als echt moderne talen (of juist hele ouderwetse)
Als je het mij vraagt is de bedoeling van een switch statement dat je elke case afzonderlijk kunt lezen, dat is in jou voorbeeld niet. Gebruik dan liever gewoon een if/else if/else reeks waarvan voor iedereen duidelijk is dat deze van boven naar onder geëvalueerd wordt.

Met 1 simpel object kom je zo mogelijk nog weg, maar zodra je verschillende types en/of meerdere objecten moet matchen zal het heel snel onleesbaar worden.
Op dat aspect is deze manier van pattern matching gewoon een nette high level abstractie die heel krachtig is en goed leesbaar blijft.
Dat is vast een kwestie van smaak, maar ik vind zo'n switch(true)-constructie volstrekt onleesbaar en een misbruik van de switch. Het is nog erger dan de niet-triviale 'fall-through'.

En met 'de meeste moderne talen' bedoel je JavaScript? JavaScript is net 4 jaar jonger dan Pyhon. Een ouwe roestbak.

"Structural pattern matching" wordt pas echt krachtig als je algebraische datatypes hebt. Haskell (en voorlopers) en sommige andere functionele programmeertalen hebben die al sinds de jaren 70, inclusief de pattern matching. Tegenwoordig wordt het plotseling ook populairder in imperatieve programmeertalen. Rust heeft ze. En de eerste stappen om het ook in Java in te voeren zijn gezet.
Ik heb weer wat geleerd vandaag!
Zoals ik het zie in de PEPs is het een vorm van switch maar met built-in assignments in de cases. Bijvoorbeeld, van https://www.python.org/dev/peps/pep-0635:
if isinstance(x, tuple) and len(x) == 2:
host, port = x
mode = "http"
elif isinstance(x, tuple) and len(x) == 3:
host, port, mode = x
# Etc.
Dit kan worden:
match x:
case host, port:
mode = "http"
case host, port, mode:
pass
# Etc.

[Reactie gewijzigd door Robtimus op 25 juli 2024 14:29]

Een switch heeft de mogelijkheid tot fall-through als je de 'break' weglaat. De implementatie van Python lijkt dat niet te kunnen.
Maar het benodigen van break geeft ook veel bugs wanneer mensen het vergeten (zoals ondergetekende)...
Ik heb wat duiding op basis van de PEP's toegevoegd en ik zie dat users hier al voorbeelden plaatsen die kunnen helpen.
Precies, was voor mij dé reden om door te klikken naar het artikel ....
Is het niet meer om het leesbaar te houden? Als je het niet cursief zet moet je er aanhalingstekens omheen zetten of zo, om de zin struktuur te geven.
Cursief omdat het Engelse tekst is. Voor Python dev's is het logische taal :-)
Ze zijn denk ik niet cursief omdat het geen gangbare woorden zijn, maar cursief om de functionaliteit naam te onderscheiden van de rest van de text.

Het zijn overigen wel gangbare termen voor programmeertalen. Maar ben het met je eens voor een site als Tweakers waar ook v el bezoekers niet kunnen programmeren het gepast was dit toe te lichten.
abbr is een hele oude HTML tag (20 jaar +), maar die gebruik je eigenlijk voor afkortingen. Ik denk dat je eerder een <span title=""> zou gebruiken. Maar goed, ik zeg dat met kennis van 20 jaar terug :-)
Misschien leuke tip voor Geachte Redactie, maar hier in de comments is het off-topic en doen ze er toch niks mee.
Versienummers die zoals hier na 3.9 'verhogen' naar 3.10 blijf ik maar kriebels van krijgen.
https://semver.org/
Given a version number MAJOR.MINOR.PATCH, increment the:
  • MAJOR version when you make incompatible API changes,
  • MINOR version when you add functionality in a backwards compatible manner, and
  • PATCH version when you make backwards compatible bug fixes.
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
Vanuit dit oogpunt vind ik het persoonlijk eigenlijk alleen maar logisch dat ze doornummeren
Dat is idd de meest gebruikte manier van versie nummering. Toch betrap ik mezelf er steeds vaker op dat ik het lastig vind om aan te geven welke wijziging in welke categorie hoort en dat ik me hardop afvraag wat nou het hele punt is.
Als ik een nieuwe versie van mijn applicatie release wil ik altijd dat die backwards compatible is en elke versie is beter (dmv nieuwe features of bugfixes) en dat ik maar een enkele identifier nodig heb om te zien welke versie een klant gebruikt. Ik zou denk ik liever gewoon gewoon een enkel versie nr willen. "My App (version 83)".
Major, minor, patch voelt steeds meer als overbodige boekhouding.
Je snapt dat 3.9 naar 4.0 vaak iets totaal anders betekend als 3.9 naar 3.10?

Zo niet kijk even naar semver.org :)
Dan interpreteer je het versienummer schijnbaar als rationaal getal. Dat is het niet. Het is gewoon een punt-gescheiden lijstje van versienummers op verschillende niveaus.

[Reactie gewijzigd door bwerg op 25 juli 2024 14:29]

Typo: rationaal
Er is niks rationeels aan versienummers :)
En hoe reageer je dan op versienummers die uit 3 of 4 delen bestaan? Je moet gewoon even af van het idee dat het getallen zijn.

Het enige probleem wat ik ermee heb is dat het alfabetisch niet goed uitkomt. Ik heb een disk waar ik installatiebestanden van software op zet en vaak in meerdere versies. Dan komt bijvoorbeeld de map met Python 3.10 tussen Python 3.1 en Python 3.2 te staan en niet achter Python 3.9.
Voelt voor mij eigenlijk wel logisch. Alternatieven (die ik inderdaad vaak genoeg zie) zijn 3.91 of 3.99, of in uiterste gevallen 4.0. Van al deze krijg ik weer 'kriebels'.
Jij hebt zeker een pesthekel aan Windows for Workgroups 3.11.
Het is in het begin inderdaad wat onwennig, maar zodra je de filosofie erachter begrijpt (semver) dan is het enorm handig juist.
Snap wat je bedoelt. Als je het puur als getal beschouwt, zijn 3.10 of zelfs 3.100 natuurlijk gewoon gelijk aan 3.1.

Punt is echter dat je de twee getallen als los van elkaar moet zien, zoals anderen al hebben gezegd. Major version en Minor/sub version worden ook vaak als losse integers opgeslagen, bijgehouden en opgeslagen.
Wat komt er bij jou na 9 dan
Ik denk dat deze versie zeer fijn zal zijn, voor mensen die beginnen met python. Aangezien die beter aangeeft, waar je een fout hebt gemaakt. Hoop dat het ook effect heeft voor programma's als salt en ansible.
Een vergelijkbaar (simpeler) voorstel voor een switch statement is jaren geleden afgewezen door de Python developers: https://www.python.org/dev/peps/pep-3103/#rejection-notice

Blij dat het alsnog is toegevoegd in een nog veel geavanceerdere vorm!
Ik kijk er wel erg naar uit om deze versie te gaan gebruiken op mijn werk. Nu nog wat collega's overtuigen dat het nu toch echt de moeite waard is om te migreren van 3.6, en wachten op stabiele 3.10 support van sommige packages die we gebruiken (nuitka)
Kwam ik ook net achter. Beetje een shitshow met packages die nog niet gesupport worden. Denk aan pandas en numpy.

Maar weer terug naar 3.9.7
Het Python-releaseteam heeft versie 3.10.0 van de programmeertaal vrijgegeven.
Scriptingtaal*
Een programmeertaal is een formele taal waarin de opdrachten die een computer moet uitvoeren, worden geschreven. Deze talen hebben een andere syntaxis en grammatica dan natuurlijke talen. Deze laatste zijn te complex en ambigu om als programmeertaal te fungeren. Code die in een programmeertaal geschreven is, dient maar op één manier te kunnen worden 'begrepen' door de computer.
https://nl.wikipedia.org/wiki/Programmeertaal

https://nl.wikipedia.org/wiki/Lijst_van_programmeertalen
Ok, en nu? Ik kan ook een specifieke alinea van Wikipedia copypasten die precies zegt wat ik wil. Het onderscheid tussen programmeer- en scriptingtalen is er niet voor niks. Een compiler werkt toch heel anders dan een interpreter, het eindresultaat van het eerste is bijvoorbeeld meteen gebonden aan een bepaalde architectuur.
Dat vind ik zelf een beetje een raar onderscheid. Als ik een C interpreter schrijf, is het dan opeens een scripttaal? En als ik een native Python compiler maak, is het dan wel een programmeertaal? En bij python is het mogelijk nog wat extra complex, want in dit geval, bij de release van Python 3.10 gaat het om zowel de language spec, de standard library als de reference implementatie van een interpreter die deze deze beiden interpreteert (CPython). Maar er zijn ook andere implementaties, zoals Jython. En een (bijna) compiler, zoals nuitka. En ergens er tussenin dan, zoals bijvoorbeeld bij JIT compilers het geval is zoals PyPy?

Ik denk dat een nuttiger onderscheid is de intentie van de taal in kwestie te bekijken. Is het bedoelt voor het automatiseren van systeembeheer- (bash, powershell) of applicatietaken (lua, autoit)? Dan is een scripttaal denk ik een goeie benaming. Ontwikkel je er complete applicaties in, dan programmeertaal.

En dan nog: In veel gevallen leent de taal zich voor beiden (python, lua, JavaScript) maar bepaalt de toepassing of iets als een 'script' of 'applicatie' telt.
Eindelijk iemand die meer doet dan een cherrypicked alineaatje van Wikipedia uitkotsen of herhalen wat allang gezegd is. :D

Je spreekt jezelf imo een beetje tegen; je hebt het over de intentie van een taal en vervolgens haal je er third-party dingen als Jython en nuitka erbij, da's natuurlijk niet eens relevant dan. ;]

Wat vind je dan van een calculator? Je kunt in Bash met de tool bc en een paar regeltjes code een aardige calculator maken, die dan de uitkomst van een som hergebruikt als 1e waarde in de volgende som. Is dat een script of een programma? En als ik er een TUI (text-based user interface) omheen zet, verandert dat dan? Of als ik er een full fledged GUI van maak (á la Windows calculator), wat dan? En als ik nu dezelfde 3 varianten met exact hetzelfde gedrag en dezelfde features maak in C (misschien gewoon lekker vies met popen() calls), waarom zou je het dan (waarschijnlijk) altijd een programma noemen?

Op basis van het eindresultaat iets een programmeer- of scripttaal noemen wordt dus lastig, want je zit dan eigenlijk al "te hoog". Het enige onderscheid wat je kunt maken is op laag niveau, hoe het door de computer behandeld wordt. Het verschil in performance tussen vooraf compiled en real-time interpreted kan toch behoorlijk zijn.
Een scripttaal is een programmeertaal

Een gecompileerde taal is ook een programmeertaal
Basically, all scripting languages are programming languages. The theoretical difference between the two is that scripting languages do not require the compilation step and are rather interpreted.

[Reactie gewijzigd door Zarhrezz op 25 juli 2024 14:29]

Volgens deze definitie is ouderwetsch BASIC (zoals bv. GW-BASIC) ook een script taal.
Heeft de Hibiscus bloem nog een speciale betekenis voor Python 3.10?
Ze komen vaker samen voor, de betekenis van de combi ken ik niet.

https://www.redbubble.com...tarrypaige/53640197.M4A2N
De Hibiscus bloem (rood) is de nationale bloem van Maleisië (Bunga Raya). Behalve dat er in Maleisië veel python programmeurs zijn is het verband met Bunga Raya voor Python niet duidelijk (niet voor mij in ieder geval).

Aanvulling:
Maleisië heeft wel de langste python soort: "de De netpython (Malayopython reticulatus) is een grote slang uit de familie pythons (Pythonidae)." ( zie:https://nl.wikipedia.org/wiki/Netpython ). Deze slang wordt door biologen niet meer (of toch weer wel) tot de pythons gerekend. Dus eigenlijk (of toch weer niet) een NEPpython.

[Reactie gewijzigd door janbaarda op 25 juli 2024 14:29]

Eindelijk verzinnen ze iets, dat duurde lang, in 2016 had ik al aangehaald dat switch krachtiger kon worden geïmplementeerd ([php] continue in een switch om conditie te her-evalueren)
Sindsdien heb ik geleerd dat het beter is dit te vermijden, omdat je achteraf toch vaak moet herschrijven vanwege de beperking.
Dat structural pattern matching lijkt enigzins op unificatie in Prolog.

Op dit item kan niet meer gereageerd worden.