De python2 à python3 : convertir un script et principales différences

PYTHON 2 n'est plus supporté depuis le 1er janvier 2020, bien qu'il puisse encore être installé sur votre système (par exemple 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. 2022.08.20 - Réécriture (2019.12.28) de la page introduisant les nouveautés apportées par python3

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 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 forçant l'encodage UTF-8.

Ce petit avatar étant 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 cependant régler l'encodage de la console en rapport avec l'encodage des scripts en python, sans quoi l'on obtient

L'utilisation de l'UTF-8 rend inutiles les fonctions unicode, u"", unichr() et decode(), qui n'existent plus en python3.

Ensuite modifier :

Mots réservés - Fonctions - Chaînes / UTF-8 - Nombres - Collections

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 et idéogrammes.

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 python 2.7.

print()

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 :

import time
for i in range(3) :
  print("*", end ="", flush =True)
  time.sleep(1)

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 '#').

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.

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 types différents, 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 et encore plus :

print("Ajout d'une chaîne", file =open("monfichier.txt", "a"))

En espérant que le fichier soit refermé au sortir du print()

input() remplace raw_input()

En python2, input() évalue une saisie et raw_input() considère l'entrée comme une simple chaîne. 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

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 ne commençant pas par '#') :

from __future__ import generators

Chaînes

UTF-8 est la norme en python3

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)

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 :

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 transeformer 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.

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

Nombres

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 le 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.

xrange() est supprimé

range() le remplace, mais en python3 , ce n'est plus une liste qui est créée mais un itérateur spécial <class 'range'>.

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

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

Certaines version de python2 retournent parfois un résultat imprécis pour round(n, d) :

>>> round(3.14159, 2)
3.1400000000000001

Ce n'est plus le cas en python3.

Collections

Dictionnaires

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 :

>>> 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.

Python 3 connaît une nouvelle forme de dictionnaire : OrderedDict, qui conserve la mémoire de l'ordre de ses éléments.

Listes

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,

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())