Avec de nombreuses distros Linux qui viennent avec Python 3 par défaut ainsi que Django et Pyramid qui annoncent bientôt ne plus supporter Python 2, il est temps de faire un point.
Python 3 est aujourd’hui majoritairement utilisé pour tout nouveau projet ou formation que j’ai pu rencontrer. Les plus importantes dépendances ont été portées ou possèdent une alternative. Six
et Python-future
permettent d’écrire facilement un code compatible avec les deux versions dans le pire des cas.
Nous sommes donc bien arrivés à destination. Il reste quelques bases de code encore coincées, la vie est injuste, mais globalement on est enfin au bout de la migration. Mais ça en a mis du temps !
Il y a de nombreuses raisons qui ont conduit à la lenteur de la migration de la communauté :
- Python 2 est un très bon langage. On ne répare pas ce qui marche.
- Il y a beaucoup de code legacy en Python 2 et ça coûte cher de migrer.
- La PSF a été trop gentille avec la communauté et l’a chouchoutée. En JS et Ruby ils ont dit “migrez ou allez vous faire foutre” et tout le monde a migré très vite.
Mais je pense que la raison principale c’est le manque de motivation pour le faire. Il n’y a pas un gros sticker jaune fluo d’un truc genre “gagnez 30% de perfs en plus” que les devs adorent même si ça n’influence pas tant leur vie que ça. Mais les codeurs ne sont pas rationnels contrairement à ce qu’ils disent. Ils aiment les one-liners, c’est pour dire.
Pourtant, il y a des tas de choses excellentes en Python 3. Simplement:
- Elles ne sont pas sexy.
- Elles ne se voient pas instantanément.
- Personne n’en parle.
Après 2 ans de Python 3 quasi-fulltime, je peux vous le dire, je ne veux plus coder en Python 2. Et on va voir pourquoi.
Unicode, mon bel unicode
On a crié que c’était la raison principale. A mon avis c’est une erreur. Peu de gens peuvent vraiment voir ce que ça implique.
Mais en tant que formateur, voilà ce que je n’ai plus a expliquer:
- Pourquoi un accent dans un commentaire fait planter le programme. Et
# coding:
- Pourquoi il faut faire
from codecs import open
et non pas justeopen
. - Pourquoi
request.get().read() + 'é'
fait planter le programme. Etencode()
etdecode()
. - Pourquoi
os.listdir()[0] + 'é'
fait afficher des trucs chelous. - Pourquoi
print(sql_row[0])
fait planter le programme ou affiche des trucs chelous. Ou les deux.
Et je peux supprimer de chacun de mes fichiers:
- Tous les
u
ou lesfrom __future__/codecs
- Les en-tête d’encoding.
- La moitié des
encode
/decode
.
Et toutes les APIS ont un paramètre encoding
, qui a pour valeur par défaut ‘UTF8’.
Debuggage for the win
Des centaines d’ajustements ont été faits pour faciliter la gestion des erreurs et le debuggage. Meilleures exceptions, plus de vérifications, meilleurs messages d’erreur, etc.
Quelques exemples…
En Python 2:
>>> [1, 2, 3] < "abc" True |
En Python 3 TypeError: unorderable types: list() < str()
of course.
En Python 2, IOError
pour tout:
>>> open('/etc/postgresql/9.5/main/pg_hba.conf') Traceback (most recent call last): File "<stdin>", line 1, in <module> IOError: [Errno 13] Permission denied: '/etc/postgresql/9.5/main/pg_hba.conf' >>> open('/etc/postgresql/9.5/main/') Traceback (most recent call last): File "<stdin>", line 1, in <module> IOError: [Errno 21] Is a directory: '/etc/postgresql/9.5/main/' |
En Python 3, c'est bien plus facile à gérer dans un try
/except
ou à debugger:
>>> open('/etc/postgresql/9.5/main/pg_hba.conf') Traceback (most recent call last): File "<stdin>", line 1, in <module> PermissionError: [Errno 13] Permission denied: '/etc/postgresql/9.5/main/pg_hba.conf' >>> open('/etc/postgresql/9.5/main/') Traceback (most recent call last): File "<stdin>", line 1, in <module> IsADirectoryError: [Errno 21] Is a directory: '/etc/postgresql/9.5/main/' |
Les cascades d'exceptions en Python 3 sont très claires:
>>> try: ... open('/') # Erreur, c'est un dossier: ... except IOError: ... 1 / 0 # oui c'est stupide, c'est pour l'exemple ... Traceback (most recent call last): File "<stdin>", line 2, in <module> IsADirectoryError: [Errno 21] Is a directory: '/' During handling of the above exception, another exception occurred: File "<stdin>", line 4, in <module> ZeroDivisionError: division by zero |
La même chose en Python 2:
Traceback (most recent call last): File "<stdin>", line 4, in <module> ZeroDivisionError: integer division or modulo by zero |
Bonne chance pour trouver l'erreur originale.
Plein de duplications ont été retirées.
En Python 2, un dev doit savoir la différence entre:
range()
etxrange()
map/filter
etitertools.imap/itertools.ifilter
dict.items/keys/values
,dict.iteritems/keys/values
etdict.viewitems/keys/values
open
etcodecs.open
- Et
md5
vshashlib.md5
getopt
,optparse
,argparse
- Cette manipulation de fichier se fait avec
sys
,os
oushutil
? - On hérite de
UserDict
oudict
?UserList
oulist
? - ...
Sous peine de bugs ou de tuer ses perfs.
Certains bugs sont inévitables, et les modules csv
et re
sont par exemple pour toujours buggés en Python 2.
Good bye boiler plate
Faire un programme correct en Python 2 requière plus de code. Prenons l'exemple d'une suite de fichier qui contient des valeurs à récupérer sur chaque ligne, ou des commentaires (avec potentiellement des accents). Les fichiers font quelques centaines de méga, et je veux itérer sur leurs contenus.
Python 2:
# coding: utf8 from __future__ import unicode_literals, division import os import sys from math import log from codecs import open from glob import glob from itertools import imap # pour ne pas charger 300 Mo en mémoire d'un coup FS_ENCODING = sys.getfilesystemencoding() SIZE_SUFFIXES = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] def file_size(size): order = int(log(size, 2) / 10) if size else 0 # potential bug with log2 size = size / (1 << (order * 10)) return '{:.4g} {}'.format(size, suffixes[order]) def get_data(dir, *patterns, **kwargs): """ Charge les données des fichiers """ # keyword only args convert = kwargs.get('convert', int) encoding = kwargs.get('encoding', 'utf8') for p in patterns: for path in glob(os.path.join(dir, p)): if os.path.isfile(path): upath = path.decode(FS_ENCODING, error="replace") print 'Trouvé: ', upath, file_size(os.stat(path).st_size) with open(path, encoding=encoding, error="ignore") as f: # retirer les commentaires lines = (l for l in f if "#" not in l) for value in imap(convert, f): yield value |
C'est déjà pas mal. On gère les caractères non ASCII dans les fichiers et le nom des fichiers, on affiche tout proprement sur le terminal, on itère en lazy pour ne pas saturer la RAM... Un code assez chouette, et pour obtenir ce résultat dans d'autres langages vous auriez plus dégueu (ou plus buggé).
Python 3:
# wow, so much space, much less import from math import log2 # non buggy log2 from pathlib import Path # plus de os.path bullshit SIZE_SUFFIXES = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] def file_size(size): order = int(log2(size) / 10) if size else 0 size = size / (1 << (order * 10)) return f'{size:.4g} {suffixes[order]}' # fstring def get_data(dir, *patterns, convert=int, encoding="utf8"): # keyword only """ Charge les données des fichiers """ for p in patterns: for path in Path(dir).glob(p): if path.is_file(): print('Trouvé: ', path, file_size(path.stat().st_size)) with open(path, encoding=encoding, error="ignore") as f: # retirer les commentaires lines = (l for l in f if "#" not in l) yield from map(convert, lines) # déroulage automatique |
Oui, c'est tout.
Et ce n'est pas juste une question de taille du code. Notez tout ce que vous n'avez pas à savoir à l'avance pour que ça marche. Le code est plus lisible. La signature de la fonction mieux documentée. Et il marchera mieux sur une console Windows car le support a été amélioré en Python 3.
Et encore j'ai été sympa, je n'ai pas fait la gestion des erreurs de lecture des fichiers, sinon on en avait encore pour 3 ans.
Il y a des tas d'autres trucs.
L'unpacking généralisé:
>>> a, *rest, c = range(10) # récupérer la première et dernière valeur >>> foo(*bar1, *bar2) # tout passer en arg >>> {**dico1, **dico2} # fusionner deux dico :) |
Ou la POO simplifiée:
class FooFooFoo(object): ... class BarBarBar(FooFooFoo): def wololo(self): return super(BarBarBar, self).wololo() |
Devient:
class FooFooFoo: ... class BarBarBar(FooFooFoo): def wololo(self): return super().wololo() |
Python 3 est tout simplement plus simple, et plus expressif.
Bonus
Evidement il y a plein de trucs qui n'intéresseront qu'une certaine catégorie de devs:
ipaddress.ip_address
pour parser les adresses IP.asyncio
pour faire de l'IO non blocante sans threads.enum
des enum de toutes sortes.functools.lru_cache
pour cacher le résultat de ses fonctions.- type hints pour vérifier la validité de son code.
- l'opérateur
@
pour multiplier des matrices avecnumpy
. concurrent.futures
pour faire des pools non blocantes propres.statistics
stats performantes et correctes.tracemalloc
trouver les fuites de mémoire.faulthandler
gérer les crash du code C proprement.
Si vous n'êtes pas concernés, ça n'est pas motivant. Mais si ça vous touche personnellement, c'est super cool.
Au passage, depuis la 3.6, Python 3 est enfin plus rapide que Python 2 pour la plupart des opérations :)
Pour finir...
Toutes ces choses là s'accumulent. Un code plus court, plus lisible, plus facile à débugger, plus juste, plus performant. Par un tas de petits détails.
Alors oui, la migration ne va pas restaurer instantanément votre érection et vous faire perdre du poids. Mais sur le long terme, tout ça compte énormément.
“Django et Pyramid qui annoncent bientôt ne plus supporter Python 3”
c’est pas python 2 plutôt ?
Oooops : au tout début de l’article “qui annoncent bientôt ne plus supporter Python 3” —> c’est 2 ;-)
Oui, mais Python3 c’est pas TuringComplïte© !
Django annonce ne plus supporter Python 3 ? Ou 2 ? ;)
Tout ce qui fait que je suis passé directement de Python 2 à Go :
gestion de la concurrence intégrée dans le language,
distribution de programmes facile,
rapidité de compilation et d’execution,
vérification de formatage par défaut,
gestion des tests par défaut,
gestion de la doc par défaut.
J’en pouvais plus de passer 3 heures à éplucher 12 docs différentes qui se contredisent pour distribuer un programme…
Je n’aime pas l’évolution que Python 3 prend…
L’asynchrone ne me convainc pas depuis que j’ai goûté aux threads légers de Go.
Bref, j’utilise toujours Python pour des scripts d’admin rapides à écrire mais plus pour des projets entiers…Pourtant il y a avait l’exemple de Perl…On ne casse pas une communauté sinon c’est la fin…et j’ai pas que cela à branler de réecrire mes progs d’il y a 10 ans et d’aller à la pêche aux modules voir s’ils sont v2 ou v3…mais je vous rassure, je trouve qu’il y a pire : le Javascript ;)
@asshole : a moins d’écrire un serveur, Go n’a quasiment aucun avantage sur Python. Analyser des données, manipuler du texte, faire des script, gérer des erreurs, faire un site web… Python est toujours plus facile à utiliser pour cela. Oui la distribution est meilleur, mais vu qu’on fait rarement des programmes avec GUI avec au final ça n’apporte pas grand chose. Quitte à apprendre un langage bas niveau et se faire chier, autant prendre rust.
@gordon:
Oui c’est une des raisons pour lesquelles je ne recommande pas son livre. Si certains points sont intéressant, il y a beaucoup de mauvaise foi. Particulièrement, Python 3 est immensément meilleur pour les débutants. C’est justement un des points principaux d’attraction. Dire le contraire est soit un mensonge, soit de l’incompétence.
@david: ouai j’ai bu
Typos:
– “Et toutes les APIS ont un paramètres encoding” –> “un paramètre”
– “Des centaines d’ajustements on été fait” –> “ont été”
– “Les fichiers font quelques centaines de méga, et jeux veux itérer sur leurs contenus.” –> “et je veux”
– “Et encore j’ai été sympas” –> “sympa”
– “la migration ne va pas réstaurer instantannément votre erection et vous faire perdre du poids” –> restaurer instantanément votre érection
Est-ce une typo ? “En Python un dev doit savoir la différence entre:” –> “En Python 2”
HS: Quitte à poster un message, un grand merci à toi Sam pour tout ces articles super intéressant (merci aussi à Max, mais j’ai l’impression de ne pas le lire souvent)
Question: je ne code pas vraiment en python même si j’ai été amené à en faire un tout petit peu. N’est-ce pas gênant de n’apprendre que python3 quand on commence ? Ne devrait-on pas d’abord galérer un peu sur le 2 histoire de mieux comprendre ?
@blackmoor : merci. Non plus maintenant, tu peux oublier Python 2. Ca ne servira pas assez. Si un jour par malchance tu dois lire ou porter une code Python 2, il sera temps de t’y pencher, mais seulement à ce moment là. Ceci dit juste lire un code Python 2 est facile si on connait Python 3. Le langage n’est pas fondamentalement différent.
Ca fait plaisir ces nouveaux posts !
Je partage clairement ton point de vue sur Python 3, surtout depuis l’arrivee 3.5
Typos reperees dans la fonction
get_data
de l’exemple Python3* une parenthèse en trop après le
glob(p)
*
if path.isfile():
=>if path.is_file():
*
path.stat.st_size
=>path.stat().st_size
Merci
Mais moi, je bosse sous CentOS 6.5, et sous CentOS 6.5 c’est encore python 2.6 par défaut … oui 2.6
On peut s’en sortir en installant les software collections mais du coup il faut envoyer 46 commandes avant de pouvoir taper du python3.
bref c’est pas gagné
Nous on veut une 2.8 ;)
Reste à ce que les distributions Linux à la traîne finissent par accepter qu’aujourd’hui, python == python3…
https://medium.com/@kevalpatel2106/why-should-you-learn-go-f607681fad65#.dpvwxez40
@Sam
Avoue, tu voulais juste vérifier qu’on avait bien tous migré vers Python 3 ? ;), une forme d’audit informel en mode shadow ;)
@Zesk06: ouai comme je le disais, le monte est injuste.
Mes palliatifs sont :
Hey super !!! De noveaux articles sur Sam & Max venez tous !!!!!!!!!!!!!!
Enfin un point exhaustif (ou presque) des différences (oserais-je dire des avantages) de python 3 par rapport à Python 2…
Ben moi je dois faire des code Python 3 sur Cent OS 7 mais j’ai pas la possibilité d’avoir toutes les lib dispos pour Python 3… –> on fait du backport des lib Python3 vers Python2 pour qu’au final on puisse faire du Python3…
Vivement 2020 et la fin officielle de Python 2 !!!!
Pu*ain le con (c’est moi), j’avais défacé l’article avec une balise “code” mal fermée… Mes plus plates excuses à tous les lecteurs de passage!
Merci pour l’article, sympa !
Yep !
Sans oublier les imports relatifs et absolus, qui clarifient bien les choses, et le fameux “1 / 2 = 0” qui a du dérouter bon nombre de néophytes.
Si vous êtes un homme, coder en python 3 multiplie par 1.5 la quantité de sperme lors d’une éjaculation.
Si vous êtes une femme, la densité de corpuscule de Krause sur le clitoris est multipliée par 1.5 également.
Vivement le python 4 !
Un article sur Nuitka de prevu ?
Pas pour le moment mais je garde l’idée en tête.
Sure! Je me suis littéralement plongé dans la programmation Python à partir de sa version 3 (et plus précisément la version 3.4), un peu parhasard, alors que ça faisait des années que je lorgnais sur ce langage. Mais la plupart des tutos et autres publications tournaient essentiellement sur la version 2 – qui rebute plus d’un débutant !
C’est génial que cette version apporte nativement des tas d’outils assez puissants, que ses performances s’améliorent et dépassent désormais la version 2 et que ça ouvre des perspectives aussi larges sur le développement (applicatif, web, etc.)
Mais plusieurs questions :
– pour Fabric bloque encore sur la version 2.7 de python et bute sur le portage en version 3 ? Manque de dev ou bien ??
– pourquoi rust plutôt que Go ? Je suis en train de me former sur Golang et j’avoue que le passage par Python 3 et Perl m’aide considérablement à comprendre aussi bien la syntaxe que la logique de ce langage…
– Que va devenir Python dès qu’il devra franchir la barre du 3.9 ? Car à la vitesse où ça passe d’une souche à l’autre, c’est pour très bientôt, non ?
En tout cas, super vos articles… Vivement le prochain !
1 – Faut demander à Fabric.
2 – Parce que Go est “ni bas ni haut niveau”. Si on veut un truc productif, on utilise Python. Si on veut du control, on utilise rust (ou c, ou c++). Go n’a d’interêt que pour sa gestion de la concurrence, mais alors autant utiliser erlang. C’est un langage qui n’est pas bon partout, mais pas le meilleur dans sa spécialité non plus. Pas très avantageux à apprendre.
3 – Python suit semver. Après la 3.9, c’est donc 3.10, 3.11, etc. Mais même au passage de la 4.0, Guido a clairement spécifié qu’il n’y aura pas la même politique que pour la 3.0 car ça avait été trop dur à gérer.
@mzk: le problème est que le mec de Fabric, Jeb Forcier, semble être un gros con, en tout cas agit comme tel. Après avoir promis Fabric2, compatible python 3 vers 2014, le mec a ensuite plus donné d’informations. développe le projet sur un dépot privé (c’est ce qu’il affirme, personne n’a pu vérifier), et ferme sans explications tous les tickets Github qui demandent des informations sur le sujet ou proposent de l’aide [1]. C’est d’autant plus inacceptable que des mecs ont porté Fabric sur Python 3 en utilisant 2to3 et c’était facile [2]. Bref, un fork compatible Python3 a été fait et il semble très bien fonctionner : https://github.com/mathiasertl/fabric/
[1] https://github.com/fabric/fabric/issues/1417 https://github.com/fabric/fabric/issues/565 https://github.com/fabric/fabric/issues/1050#issuecomment-140577614
[2] https://github.com/fabric/fabric/issues/1424#issuecomment-179552669
C’est pas être un con ça, c’est pas avoir le temps et être dépassé par son projet.
Je suis récément tombé sur ce genre de press anti-python : https://learnpythonthehardway.org/book/nopython3.html !!!
J’avoue que je ne sais pas quoi en penser certain argument sont ou semble pertinent mais je n’ai pas le recul (ou la profondeur) pour savoir en tirer parti… (l’auter non plus d’ailleurs).
Qu’en pensez-vous ? Loin de moi l’idée de troller c’est un sujet sensible surtout vu la manière dont l’article est rédigé mais je shouaiterais savoir un petit peu ce que vous en pensez tous amis Pythoniste !
En tout cas, j’adore les débats qu’a provoqué cette nouvelle version, c’est passionnant !
@Abject : essentiellement du FUD. La question python 2/3 a été largement débattue, les bons et mauvais points sont listés, connus et traités. Faut passer à autre chose.