La communauté Python est assez d’accord ces derniers temps. Maintenant que le plus gros de la débâcle Python2/3 est derrière nous (en attendant le contre coup des retardataires de 2020) et qu’on a un modèle d’IO async bien clean, les trucs qui fâchent sont assez clairement délimités:
- Le packaging.
- Le multi-CPU.
- Les perfs.
Sur ces questions, du travail est activement en cours.
Pour le multi CPU, un modèle propre permettra d’utiliser plusieurs interpréteurs en parallèle en partageant des valeurs
sans avoir à les sérialiser.
Pour les perfs, se sera un taff plus long, mais:
- Les opérations sur les bytes ont des perfs bien améliorées pour la 3.6.
- Victor Stinner et Yury Selivan travaillent sur des trucs très cools. Jetez un coup d’œil aux mailling lists sur les sujets “FAT Python”, “PEP 509/510/511″ et “Explicit variable cappture list” pour voir tout un tas d’optimisations prévues, ou permettant d’en prévoir.
- Pyjion, la JIT de Microsoft, un des projets les plus prometteurs pour accélérer Python, commence à faire parler d’elle.
- Dropbox, chez qui travaille Guido Van Rossum, bosse sur son implémentation de Python également.
- Et bien entendu Pypy continue son bonhomme de chemin.
Bref, y a du potentiel.
Pour le packaging, les wheels vont enfin arriver sous Linux, ce qui fait qu’on pourra bientôt pip installer des binaires partout. nuikta, le compilateur Python, supporte maintenant await/async.
On est sur la bonne route.
L’année 2016 va être trop cool, et dans mon enthousiasme, j’aimerais écrire à propos de choses que j’aimerais vraiment voir arriver dans Python.
try/except error inline
Beaucoup de codes en Python ressemblent à ça :
try:
val = faire un truc
except MonErrorALaNoix:
val = "valeur par default" |
Par exemple :
try:
val = stuff[index]
except (IndexError, TypeError):
val = None |
Ce sont des opérations si courantes qu’on a plein de raccourcis comme dict.get
ou next(i, None)
. En effet, en Python try
/except
n’est pas juste un mécanisme de gestion d’erreur, c’est un mécanisme de contrôle de flux à part entière.
Car franchement, ça fait chier de se taper 4 lignes pour écrire ça. En effet, on a bien les expressions ternaires pour les if
/else
:
val = truc if bidule else machine |
Et bien il exist un PEP (rejeté) qui propose ça:
val = faire un truc except MonErrorALaNoix: "valeur par default" |
J’adore. C’est pratique, générique, propre.
Bien entendu ça peut être abusé, comme les expressions ternaires, pour faire de la merde illisible. Mais j’ai rarement vu le cas pour les précédentes, donc ça devrait aller.
slices sur les générateurs
Les générateurs, c’est formidable. C’est iterable. On peut les utiliser partout où on utilise les listes.
Sauf si on doit les limiter en taille.
Alors là, c’est relou.
Par exemple, récupérer les carrés des nombres pairs entre 0 et 100, puis limiter le tout a 10 éléments après les 5e:
from itertools import islice
g = (x * x for x in range(100) if x % 2 == 0)
g = islice(g, 5, 15) |
Ca serait tellement plus simple de pouvoir faire:
g = (x * x for x in range(100) if x % 2 == 0)[5:10] |
callable dans les slices
Si vous voulez tous les carrés des nombres au-dessus de 5, vous pouvez faire:
(x * x for x in numbres if x > 5) |
Mais si vous voulez tous les nombres à partir du moment où vous rencontrez un nombre au-dessus de 5 ?
from itertools import dropwhile
numbers = dropwhile(lambda x: x > 5, numbers)
(x * x for x in numbres) |
Alors certes, je ne suis pas pour transformer Python en Haskel et balancer des formules magiques comme:
(x * x for x in numbers[a -> a > 5]) |
Mais juste m’éviter l’import et pouvoir faire ça:
def start(x):
return x > 5
(x * x for x in numbers[start:]) |
Ca serait cool.
with var dans les expressions génératrices
Je suis hésitant sur cette feature car c’est très tentant de faire de one liners trop longs avec, mais:
(x.split()[1] for x in truc if x.split()[1] == 1) |
C’est con de faire 2 fois split
quand même.
(y for x in truc
with x.split()[1] as y
if y == 1) |
Bon, ça peut rapidement devenir illisible, donc à méditer.
async optionnel
Je cherche toujours à comprendre pourquoi async
est nécessaire.
Avec les générateurs, la présence de yield
fait détecter automatiquement la fonction comme fonction génératrice.
Avec await
, ça devrait être pareil:
def stuff():
await bidule # bim, c’est une coroutine ! |
Pas besoin de async
. Si on veut faire une coroutine sans un seul await
, il y a toujours @asyncio.coroutine
.
async
reste très utile pour async for
et async with
, mais perso j’aurais préféré avoir un await with et un await for et pas de async
.
Après, le prenez pas mal hein. J’adore async
/await
. Vive asyncio
! Mais la syntaxe pourrait être plus naturelle, plus proche du comportement des générateurs, d’autant que ça se base dessus.