De python2 à python3 : convertir un script et principales différences
PYTHON2 n'est plus supporté depuis le 1er janvier 2020, bien qu'il puisse encore être installé sur votre système (par exemple python2.7 avec Debian 11 Bullseye). Il est conseillé de commencer tout nouveau développement avec python3. Cette page devrait vous aider à adapter d'anciens scripts écrits en python2 ; voyez ici pour tkinter.
Rév. 2023.01.11 - Réécriture (2019.12.28) de la page introduisant les nouveautés apportées par python3
Mots réservés - Fonctions - Chaînes / UTF-8 - Nombres - Collections
Il existe une application (non python) appelée 2to3, disponible notamment sur Debian, et qui transforme les scripts python2 en scripts python3. Elle semble assez difficile à utiliser, s'arrêtant déjà sur le fait que l'encodage du script est en iso-8859-15 (latin-9) alors que l'application s'attend à de l'UTF-8. Il faut charger le script avec un éditeur de texte et le «sauvegarder sous» en spécifiant l'encodage UTF-8.
Ce problème résolu, l'application retourne :
ModuleNotFoundError: No module named 'lib2to3.fixes.fix_idiom'
Et c'est bien dommage. Si vous voulez transformer vous-même un ancien script python2 en python3 «à la main», il faut d'abord changer l'entête (cas de UNIX) :
#! /usr/bin/python -Qnew # -*- encoding: latin-9 -*- from __future__ import xxx
en
#! /usr/bin/python3
L'encodage sera alors automatiquement en UTF-8, sauf si l'on garde l'une des lignes suivantes :
# -*- encoding: latin-1 -*- # -*- encoding: cp1252 -*- # -*- encoding: latin-9 -*-
Il faut vérifier que le script (sauvegarder sous...) partage le même encodage que la console sans quoi l'on obtient :
- Å“ pour un couple d'octets codant un caractère UTF-8 que la console en codage octet (win1252, latin1, latin-9...) ne peut interpréter
- � pour des octets que la console en UTF-8 ne peut interpréter
L'utilisation de l'UTF-8 rend inutiles les fonctions unicode(), u"", unichr() et decode(), qui n'existent plus en python3.
Ensuite modifier :
- print expression en print(expression)
- input() en eval(input())
- raw_input() en input()
- file() en open() (les deux coexistent en python2)
- execfile() en exec(open("myfile").read())
- …puis lancer le script dans une console, guetter les erreurs et les corriger.
Pour plus d'informations, voir la page consacrée à python3.
Mots réservés
Les expressions suivantes ne peuvent pas servir de nom de variable ou de fonction :
and · as · assert · break · class · continue · def · del · elif · else · except · exec · False · finally · for · from · global · if · import · in · is · lambda · None · not · or · pass · print · raise · return · True · try · while · with · yield
Par contre, les noms des variables et fonctions peuvent en python3 comporter des lettres accentuées, caractères non latins et idéogrammes. À ne pas utiliser dans le cadre d'un développement international.
True et False ne sont pas réservés en python2.7 (ce qui fait qu'il peut y coexister une variable ou une fonction du même nom) mais le deviennent en python3.
with et as ont été ajoutés en python2.6 et sont conservés en python3
exec et print sont réservés en python2, mais libérés en python3 :
exec → exec()
Le mot réservé exec est remplacé par la fonction interne exec() :
>>> exec("from math import sin, pi ; print(sin(pi /4))") 0.7071067811865475
Remarque : exec et exec() sont synonymes en python2.7.
print()
Pour plus de détail sur print(), voir cette page
Le «mot réservé» print de python2 est remplacé par la fonction print() en python3 :
print("Eggs & spam") # remplace print "Eggs & spam" de python2 print("J'ai", 23, "ans", sep=" - ") # remplacer l'espace (séparateur par défaut) par une chaîne print("Eggs", end="/") # remplace le saut de ligne par une chaîne.
Attention : une suite de print(chaine, end="") n'est affichée qu'à partir du premier print() contenant un saut de ligne, sauf en utilisant flush =True, ce qui permet par exemple de réaliser une barre de progression :
import time for i in range(10) : print("*", end ="", flush =True) time.sleep(.3)
Pour un affichage plus élaboré sur la console, voir le module curses.
Remarque : il est déjà d'utiliser print() depuis python2.6 grâce à from __future__ import print_function (sur la première ligne du script ne commençant pas par '#').
Depuis python2.7, il est possible de remplir une chaîne lacunaire sans les numéros d'ordre :
>>> "{} et {}".format('Romeo', 'Juliette') Romeo et Juliette
Fonctions supprimées ou déplacées
from __future__ import
from __future__ est nécessaire en début de script de python2.x pour bénéficier d'évolutions de python3.
- from __future__ import division pour la division "réelle" (sinon 7/4 donne 1)
- from __future__ import print_function pour utiliser print("spam") plutôt que print "spam"
- from __future__ import unicode_literals pour utiliser l'unicode par défaut
- from __future__ import absolute_import pour permettre une utilisation plus souple de import
- from __future__ import generators pour les générateurs (en python2.2 seulement)
apply() est supprimé en python3
Dépréciée en python2, cette fonction y est déjà remplaçable par l'utilisation de * et ** pour la réception d'une séquence et d'un dictionnaire optionnel.
#! /usr/bin/python2.7 def quoi(a, b, x, y): return a, b, x, y print(apply(quoi, (24, 36), { "x": 7, "y": 19}))
donne :
(24, 36, 7, 19)
cmp() est supprimé en python3
Voir discussion et recette sur cette page
cmp(x, y), qui renvoie -1 si x < y, +1 si x > y et 0 si x == y, est supprimée en python3.
En python2, cmp(x, y) a pour particularité d'accepter de comparer des valeurs de classes (ou types) différentes, avec des réponses stéréotypées (0 < {} < [] < "" < ()).
execfile() est supprimé en python3
La fonction execfile() de python2 peut être remplacée par :
exec(open(nomdefichier).read())
file() est supprimé en python3
Si python2 connaît également la fonction file(), open() est l'unique manière d'ouvrir un fichier en python3.
print >> fd, data est remplacé par print(data, file =fd)
En python2, une façon simple de rediriger les sorties print vers un fichier-texte sans utiliser le module sys :
fd =open("log.txt",'a') # ouverture en ajout d'un fichier print >> fd, "Salut, tout le monde" # sortie vers log.txt (avec fin de ligne) descr.close() # inscrit les données et ferme le fichier
C'est tout aussi simple en python3 :
with open("mon_fichier.txt", "a") as fd : # également à partir de python2.6 print("Salut, tout le monde", file =fd)
(fichier refermé à la fin de la structure), ou encore :
print("Ajout d'une chaîne", file =open("monfichier.txt", "a"))
raw_input()est remplacé par input()
En python3, raw_input() de python2 est supprimé et remplacé par input() pour la simple saisie de chaînes. Pour évaluer la chaîne saisie, utiliser eval() :
>>> print(eval(input("Entrer une division: "))) Entrer une division : 3/4 0.75
En python2, input() évalue une saisie et raw_input() considère l'entrée comme une simple chaîne.
intern("") est déplacée dans le module sys
En Python2, la fonction var =intern("") inscrit la variable-chaîne parmi les variables du système, ce qui la rend plus rapidement mobilisable (je n'ai pas réalisé de test). En Python3, il faut utiliser sys.intern().
import sys a =sys.intern("Une chaîne")
reduce() est déplacée dans le module functools
La fonction interne reduce() de python doit en python3 être importée du module functools.
>>> import functools >>> functools.reduce(lambda x, y : x *y, range(1, 7)) 720
Il s'agit d'une factorielle, tous les nombres issus de range(1, 7) étant multipliés l'un après l'autre. Bien que que Guido Van Rossum se repente d'avoir introduit la fonction lambda, celle-ci n'a pas encore disparu.
En python2, l'instruction yield n'est pleinement disponible que depuis la version 2.3 ; elle n'est disponible avec la version 2.2 qu'à condition de préciser en début de programme (future est précédé et suivi de deux tirets bas, et doit figurer sur la première ligne non vide ne commençant pas par '#') :
from __future__ import generators
Chaînes
UTF-8 est la norme en python3
type("") retourne <class 'str'>
En python2, type("") retournait <type 'str'>
En python3, un script considère par défaut que les chaînes sont en UTF-8, ce qui signifie qu'il n'est plus nécessaire d'inscrire # -*- coding: utf-8 -*- en début de script.
N'oubliez pas de sauvegarder vos scripts en UTF-8 et de paramétrer la console en ce sens. Pour tout autre encodage que l'UTF-8, il faut le préciser avec (par exemple iso8859-15) :
# -*- coding: latin-9 -*-
Note : la différence de poids de fichier texte en français entre les encodages iso-8859-15 (latin-9) et UTF-8 d'un même texte est assez minime, environ 5% de plus pour le second pour un texte français. Ce n'est pas le cas si vous utilisez des textes en arabe ou en grec (deux octets par lettre en UTF-8 contre un octet en iso-8859-7 ou iso-8859-6) ou en idéogrammes ou kanas (trois octets, contre deux dans les encodages spécifiques aux langues asiatiques).
Ce passage en UTF-8 implique des changements importants dans la manipulation des chaînes : pour la chaîne ch="côté", ch[3] vaut le caractère é. C'est extrêmement utile lors de décomposition de chaînes. En python3 :
#! /usr/bin/python3 chaine="côté" print(len(chaine)) # la réponse est 4 print(chaine[1]) # la réponse est 'ô'
Le codage des caractères accentués étant différent en python2, les résultats sont différents :
#! /usr/bin/python2 # -*- coding: utf-8 -*- chaine="côté" print(len(chaine)) # la réponse est 6: 'ô' et 'é' valent chacun deux octets print(chaine[1]) # la réponse est '�', premier octet (non UTF-8) du 'ô'
decode() n'existe plus en python3.
repr(objet) conditionne un objet pour son affichage complet sous forme de chaîne, c'est par exemple intéressant pour sauvegarder les variables dans un fichier. En python3, cela donne :
>>> a ="C'est déjà ça" >>> a ; print(a); print(repr(a)) "C'est déjà ça" C'est déjà ça "C'est déjà ça"
En python2 / UTF-8 :
>>> a ="C'est déjà ça" >>> a; print(a); print(repr(a)) "C'est d\xe9j\xe0 \xe7a" C'est déjà ça "C'est d\xe9j\xe0 \xe7a"
Le type 'bytes' (octet)
type(b"") retourne <class 'bytes'>
En python2, type(bytes("!")) retournait <type 'str'>
S'il est possible en python2 de réaliser une chaîne d'octets de chr(0) à chr(255) pour sauvegarder des données «binaires», ce n'est plus possible en python3, où chr(255) n'est plus un octet mais le caractère unicode 255, soit le "ÿ", qu'UTF-8 code en deux octets.
Une façon d'assembler des octets est de passer par le type 'bytes', déjà disponible en python2.6. Cela peut se faire de cette façon :
- accumulation d'octets de 0 à 255 dans une liste : li=[12, 156, 27, 40, 86]
- la conversion en bytearray : ba =bytes(li)
- la sauvegarde dans un fichier binaire : open("monfichier","wb").write(ba)
Une autre façon est de construire le bytearray petit à petit :
>>> ba =b""; ba +=b"A"; ba +=b"\x217"; ba b'A!7'
Attention : b"\x217" représente deux bytes : le premier \x21 (b'!') et le second est l'ASCII du caractère 7.
Mais le plus simple est de constituer une liste pour ensuite la transformer en objet 'bytes' :
>>> bytes([0,255,45,95,250])
Python2 dispose d'un objet unicode et d'une fonction unicode(), et même si un caractère spécial est codé en plusieurs octets, sa longueur unicode vaut 1 :
>>> u"œ"; type(u"œuf"); len(u"œuf") u'\u0153uf' <type 'unicode'> 3
Python3 ne dispose plus du type unicode, les chaînes y étant par défaut «unicode», et ce qui suit n'a plus de pertinence
Transformation (python2 uniquement !)
unicode("chaine","encodage") retourne un objet unicode utilisant l'encodage spécifié (la longueur de la première chaîne vaut 5, celle de la seconde chaîne est de 3) :
unicode("43€","latin-1") u'43\xe2\x82\xac' >>> unicode("43€","utf-8") u'43\u20ac'
Cela permet de modifier le type de codage d'une chaîne.
>>> u"é" in unicode("pétunia","utf-8") : True
unichr(entier) retourne un caractère unicode à partir d'un entier (ici en hexadécimal) :
>>> unichr(0x20ac) €
En python2, print("é".isalpha()) renvoie False, mais True en python3.
Nombres
type(0) retourne < class 'int'>
En python2, type(0) retournait <type 'int'> ; type(1L) retournait <type 'long'>
long() est supprimé en python3, puisque tous les entiers sont illimités
En python2, les entiers sont définis de -2 147 483 648 à 2 147 483 647, et un L est nécessaire en fin de nombre pour que la variable soit reconnue comme «entier long». En python3, le suffixe L et la fonction long() sont abandonnés, les entiers sont tous illimités.
coerce() est supprimé en python3
En python2, a, b =coerce(a, b) ajuste les variables numériques dans la classe (ou type) le plus englobant, avec la hiérarchie complexe > float > long > int
>>> print coerce(5, 2e3), coerce(0, 2 +1j) (5.0, 2000.0) (0j, (2+1j))
<> est supprimé en python3
Déjà déprécié en python2, <> est supprimé en python3, où != est le seul opérateur testant l'inégalité.
0o devient le préfixe octal en python3
En python3, la chaîne octale commence nécessairement par 0o43 ou 0O43 (le chiffre 'zéro' et la lettre 'o', minuscule ou majuscule). L'expression 077 (seulement préfixée avec zéro) n'est plus reconnue comme nombre octal et engendre l'erreur invalid token.
Divisions
En python2, la division de deux entiers est entière: 7 /4 retourne 1. Un point après un nombre force Python à considérer la réponse comme décimale :
>>> 7. /4 1.75
En python3, la division / n'est plus entière : 7/4 vaut 1.75 . La division entière continue à être codée //.
>>> 7 //4 ; -7 //4 1 -2
#! /usr/bin/python -Qnew permet d'utiliser / pour la division non entière. -Qnew est inutile et interdit en python3.
Il est possible en python2 d'utiliser / comme diviseur non entier en mentionnant en début de programme (future est entouré de part et d'autre de deux "soulignés") :
>>> from __future__ import division >>> 7 /4 1.75
round() est plus précis en python3
Python3 et python2.7 limitent l'affichage du nombre à la décimale demandée de façon stricte :
>>> round(3.14159, 2) 3.14
Certaines version de python2 retournaient parfois un résultat imprécis pour round(n, d) :
>>> round(3.14159, 2) 3.1400000000000001
Pour éviter un biais, python3 arrondit vers le nombre pair le plus proche :
>>> round(2.5) ; round(3.5) 2 4
Python2.6 arrondit le chiffre 5 par excès :
>>> round(2.5) ; round(3.5) 3 4
Collections
Listes
type([]) retourne < class 'list'>
En python2, type([]) retournait <type 'list'>
Rappelons la façon de s'assurer qu'une variable est une liste (== "<class 'list'>" ne fonctionne pas) :
var =[0, 1, 2, 3] ; print(type(var) ==type([]))
xrange() est supprimé (le nom, pas la fonction)
range() le remplace, mais en python3 , ce n'est plus une liste qui est créée mais un itérateur spécial <class 'range'>. Pour obtenir une liste comme en python2, il suffit d'écrire list(range()) en python3 :
>>> [3, 5, 7, 9]
De la même manière, en python2, beaucoup de réponses de fonctions internes ou de méthodes renvoient des listes [], maintenant remplacées par des itérateurs spécifiques. Par exemple,
- les fonctions internes map(), filter()… renvoient des objets de classes (ou types) map ou filter
- les méthodes .keys(), .values() et .items() des { dictionnaires } renvoient les objets de classes (ou types) dict_keys, dict_values et dict_items.
Cela signifie que le référencement par index xx[n] et des méthodes telles que .pop() ne sont plus applicables en python3 ; il est possible de créer une liste à partir d'un de ces objets :
liste_cles=list(Mon_Dico.keys())
Dictionnaires
type({}) retourne < class 'dict'>
En python2, type(0) retournait <type 'dict'>.
has_key() est supprimé en python3
python2 utilise has_key() pour vérifier qu'une clé existe dans un dictionnaire :
>>> dico ={"a" : 12 , "b" : 34} ; dico.has_key("a") True
Vérifier qu'une expression est une clé d'un dictionnaire est maintenant plus simple (existe déjà au moins depuis python2.7) :
>>> dico ={"a" : 12 , "b" : 34} ; "a" in dico True
max() et min() sont supprimés pour les dictionnaires
max() et min() ne fonctionnent pas pour comparer deux dictionnaires, contrairement en python2.
Python3 connaît une nouvelle forme de dictionnaire : OrderedDict, qui conserve la mémoire de l'ordre de ses éléments.