PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Evolution de Python 21

samedi 30 janvier 2016 à 21:31

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:

Sur ces questions, du travail est activement en cours.

Pour le multi-core, Eric Snow propose un modèle propre qui permettra d’utiliser plusieurs interpréteurs en parallèle en partageant des valeurs sans avoir à les sérialiser.

Concernant l’amélioration des perfs, ce sera un taf plus long, mais:

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 sur les 3 OS majeurs. Peut-être même BSD qui sait.

Nuikta, le compilateur Python, supporte maintenant await/async. J’ai beaucoup d’attentes envers ce projet, ça mérite un don du mois :)

On est sur la bonne route.

L’année 2016 va être trop cool, et dans mon enthousiasme, je vais é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 existe 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 le 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 Haskell 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 des 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:

def stuff(): # pas de machin def
    yield bidule # python comprends que c'est un générateur

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.

On peut imaginer l’inverse aussi : tabler sur “explicit is bettern than implicit” et rajouter un gen def pour les générateurs et obtenir la parité.

Après, le prenez pas mal hein. J’adore async/await. Vive asyncio ! Mais la syntaxe pourrait être plus cohérente, plus proche du comportement des générateurs, puisqu’un coroutine n’est qu’un générateur spécialisé.