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, août 2021). Tout nouveau développement doit donc se faire en python3. Cette page devrait vous aider à adapter d’anciens scripts écrits en python2 ; voyez ici pour tkinter.
Rév. 2023.05.10 - Réécriture de la page (2019.12.28) introduisant les nouveautés apportées par python3
Liens internes
apply() · as · bytes · chaines · cmp() · comparaisons · decode() · dictionnaires · divisions / et // · encodage · ensembles · exec · execfile() · False · file() · filter() · from __future__ · has_key() · différence != et <> · input() · intern() · .isalpha() · .items() · itérateurs · .keys() · listes · long() · map() · max() · min() · nombres · octal · print · print >> · raise · range() · raw_input() · reduce() · round() · set() · True · type() · unichr() · unicode() · .values() · variables · with · xrange()
Application 2to3
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 (latin9) 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.
Transformation «à la main»
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: latin9 -*- from __future__ import print_function
en
#! /usr/bin/python3
L’encodage du script (et de la console) devra alors être en UTF-8, sauf si l’on garde par exemple l’une des lignes suivantes :
# -*- encoding: latin1 -*- # -*- encoding: cp1252 -*- # -*- encoding: latin9 -*-
Il faut ensuite
- sauvegarder le script dans l’encodage choisi (sauvegarder sous…)
- vérifier paramétrer la console dans le même encodage. Exemples de problème :
- Å“ est le couple d’octets codant le caractère œ codé en UTF-8 que la console en codage octet (win1252, latin1, latin9…) ne peut interpréter en deux caractères distinct
- � est un octet qui code un caractère accentué en win1252, latin1, latin9…, que la console en UTF-8 ne peut interpréter
unichr() est supprimé
Le fait que les chaînes sont traitées en interne en UTF-8 est un changement majeur en python3.
- unichr() doit être remplacé par chr() qui vaut pour tous les «points de code», bien au delà de 255, voir la page des caractères.
- les fonctions unicode(), les objets u"", le type 'unicode' et unichr() 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.
Note : en python2, print() devenait obligatoire avec la directive from __future__ import print_function
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
True et False n’étaient pas réservés en python2.7 (ce qui fait qu’il pouvait 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 étaient réservés en python2 mais ne le sont plus en python3. print() était déjà possible en python2.5 avec from __future__ import print_function (première ligne du script ne commençant pas par ’#’) ; exec() et print() coexistaient en python2.6 avec print et exec.
Fonctions
exec est remplacé par 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à possible 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 avec .format() sans les numéros d’ordre :
>>> "{} et {}".format("Romeo", "Juliette") Romeo et Juliette
Depuis python3.4, une chaîne de formatage f"{data:format}" est disponible :
>>> f"{12.5:10.2e} en notation 'exponentielle'" " 1.25e+01 en notation 'exponentielle'"
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é
Dépréciée en python2, cette fonction y est déjà remplaçable par l’utilisation de * et ** pour la réception des éléments d’une séquence (liste, tuple ou chaîne) ou d’un dictionnaire. Dans l’utilisation d’ apply(), le nombre d’éléments envoyés doit être égal au nombre de variables réceptrices, et les noms de clés doivent aux noms de variables :
#! /usr/bin/python2.7 def quoi(a, b, x, y): return a, b, x, y print apply(quoi, "wz", {"x":27, "y": 19})
donne :
('w', 'z', 27, 19)
Python3 n’accepte plus que ce passage de groupes de paramètres :
def quoi(*tup) : print(tup) print(type(tup)) quoi(1, 2, 3)
…ce qui renvoie :
(1, 2, 3) <class 'tuple'>
Une suite d’affectations sera reçue comme un dictionnaire si la variable de réception est préfixée par ** :
def quoi(**dic) : print(dic) print(type(dic)) quoi(a=1, b=2, c=3)
Il est possible de composer les deux, les valeurs passées en paramètres doivent alors précéder les affectations.
Comparaisons
cmp() est supprimé en python3
Il est facile de recréer la fonction cmp() pour comparer des nombres (voir la discussion sur cette page) :
def cmp(x, y) : return (x > y) - (x > y)
En python2, cmp(x, y) renvoyait -1 si x < y, +1 si x > y et 0 si x == y ; cmp(x, y) acceptait de comparer des valeurs de classes / types différents.
<, <=, > et >=)
Plusieurs changements importants en python3, qui devient très difficile concernant les hiérarchies.
Les comparaisons entre types différents ne sont plus permises
En python2, les réponses aux comparaisons d’objets de classes différentes sont stéréotypées (mais les complexes refusent les relations d’ordre (<, >, <=, >) avec des entiers, «réels» ou complexes) :
0 < {} < [] < set() < "" < tuple()
En python3, seules les comparaisons entre objets de même classe sont permises, mais pour les ensembles, < signifie «être inclus» et > «contenir». Les complexes continues à ne pas vouloir être comparés entre eux, sauf avec == et !=.
Python2 admettait la comparaison de dictionnaires, par paire clé/valeur et selon leurs longueurs :
>>> {1:3, "b":3} < {"d":4, 1:3} True >>> {1:3, "b":3, "a":12} < {"d":4, 1:3} False
Les comparaisons de dictionnaires sont interdites :
>>> {"1":2} < {"3":5} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: '<' not supported between instances of 'dict' and 'dict'
En python2, les clés, d’abord mises en ordre alphabétiques, étaient comparées.
Il en résulte que pour que deux listes soient comparables, il faut que les valeurs soient de même classe / type à chaque rang, et ne pas contenir de dictionnaires :
>>> ["2", 3] < ["1", 6] False >>> [3, "2"] < ["1", 6] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: '>' not supported between instances of 'str' and 'int'
Notes : les comparateurs == et != fonctionnent dans tous les cas.
Il est quand même possible de mettre de l’ordre dans les données d’un dictionnaire si les clés sont toutes de même classe / type :
>>> mondic ={"c":5, "a":3, "b":2} ; list(sorted(mondic.items())) {'a': 3, 'b': 2, 'c': 5}
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 connaissait également la fonction file(), open() est l’unique manière d’ouvrir un fichier en python3.
Toutes les versions de python3 connaissent également une structure pour l’ouverture d’un fichier :
with open(fichier.txt", "w") as descr : descr.write("montexte")
...même si open(fichier.txt", "w").write("montexte") est encore plus court.
Python2 charge un fichier texte en acceptant les deux octets possibles pour les fins de ligne (Windows : \r\n (octets 13 et 10), MacOS9 :\r (octet 13)). Python3 convertit les fins de ligne de tous les systèmes en caractères \n (octet 10) et, supposera-t-on, les convertit automatique dans les fins de lignes du système-hôte.
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"))
L’intérêt (relatif) de la méthode avec fd.close() est peut-être d’ouvrir un fichier en écriture ou en ajout en début de script, d’y ajouter des données au cours du script et de le fermer à la fin. Mais il est possible de stocker ces données dans une variable et de ne la sauvegarder qu’à la fin.
raw_input() est remplacé par input()
En python2, raw_input() servait à saisir une chaine tandis que input() en évaluait la saisie :
>>> raw_input("Saisir un calcul: ") Saisir un calcul: 3 *5 +2 '3 *5 +2' >>> input("Saisir un calcul: ") Saisir un calcul: 3 *5 +2 17
En python3, raw_input() est supprimé et toute saisie de chaîne se fait avec input(). Pour évaluer la chaîne saisie, on doit utiliser eval() :
>>> input("Saisir un calcul: ") "Saisir un calcul: 3 *5 +2 '3 *5 +2' >>> eval(input("Saisir un calcul: ")) "Saisir un calcul: 3 *5 +2 17
intern("") est déplacé dans le module sys
En Python2, la fonction var =intern("") inscrivait une chaîne parmi les variables du système, censées être plus rapidement mobilisables (aucun test n’a été concluant). En Python3, il faut utiliser sys.intern().
import sys a =sys.intern("Une chaîne")
reduce() est déplacé dans le module functools
La fonction interne reduce() de python2 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.
Nombres
long() et le suffixe L sont supprimés
Avant la version python2.4, les entiers étaient limités de -2 147 483 648 à +2 147 483 647. Pour permettre une variable représentant un entier illimité, il fallait préalablement utiliser long(), le nombre produit étant terminés par un L. Depuis la version 2.4, il n’y a plus de problèmes lorsqu’un entier dépasse les limites : si un résultat se situe en dehors de cet intervalle, L suit automatiquement le nombre : var =100000 *100000 équivaut à 10000000000L. Le L est supprimé en python3, tous les entiers étant illimités.
En python2, type(0L) retournait <type 'long'> entier illimité
En python3, les entiers sont tous illimités et sont des objets <class 'int'>.
coerce() est supprimé
En python2, a, b =coerce(a, b) ajustait 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))
Aucune idée de l’utilité de cette fonction, qui a été simplement abandonnée en python3, peut-être pour la même raison que les comparaisons entre types (sauf entre entiers et «réels») ne sont plus permises.
<> est supprimé
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 python2, une chaîne octale commence par un zéro 0 (06.5 est néanmoins interprété comme le réel 6.5 en base 10), mais python2.6 et 2.7 acceptent la notation 0o43.
En python3, la chaîne octale commence nécessairement par 0o ou 0O (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 était entière : 7 /4 retournait 1. Un point après un nombre forçait Python à considérer la réponse comme décimale :
>>> 7. /4 1.75
Il était 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
Une autre façon était une déclaration en fin de première ligne
#! /usr/bin/python -Qnew
En python3, la division / retourne un «réel» dans tous les cas :
>>> 8 /2 ; type(8 /2) 4.0 <class 'float'>
La division entière continue à être codée //. Attention cependant aux résultats négatifs :
>>> 7 //4 ; -7 //4 1 -2
round() est plus précis
Certaines versions de python2 retournaient parfois un résultat imprécis pour round(), mais toujours sous forme «réelle» même avec python2.7 :
>>> round(3.14159, 2) ; round(2143, -2) ; type(round(3.14)) 3.1400000000000001 2100.0 <type 'float'>
Python3 limite l’affichage d’un nombre à la décimale demandée de façon précise et sous forme d’entier le cas échéant :
>>> round(3.14159, 2) ; round(2143, -2) ; type(round(3.14)) 3.14 2100 <type 'int'>
Python2 arrondissait toujours le chiffre .5 par excès :
>>> round(2.5) ; round(3.5) 3 4
Python3 arrondit le .5 vers le nombre pair le plus proche :
>>> round(2.5) ; round(3.5) 2 4
Pour une explication, voir round() sur la page python3.
Chaînes
UTF-8 est la norme en python3
En python2, les scripts n’acceptaient par défaut que l’encodage ASCII, limité aux lettres non accentuées, les chiffres, la ponctuation usuelle et les opérateurs habituels.
_0 | _1 | _2 | _3 | _4 | _5 | _6 | _7 | _8 | _9 | _A | _B | _C | _D | _E | _F | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
\x0_ | ||||||||||||||||
\x1_ | ||||||||||||||||
\x2_ | ||||||||||||||||
\x3_ | ||||||||||||||||
\x4_ | ||||||||||||||||
\x5_ | ||||||||||||||||
\x6_ | ||||||||||||||||
\x7_ |
Le lancement d’un script comportant une lettre accentuée :
#! /usr/bin/python2.7 # pas de lettre accentuée, même dans un commentaire ! print("Salut, tout l'monde")
retournait le message d’erreur suivant :
File "./essai.py", line 2 SyntaxError: Non-ASCII character '\xea' in file ./0.py on line 2, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
Les francophones utilisaient donc souvent une directive d’encodage permettant les lettres accentuées courantes :
#! /usr/bin/python2.7 # -*- coding: latin1 -*- print("Ça va plutôt bien")
pour obtenir :
Ça va plutôt bien
Pour se limiter au français, on avait le choix entre les synonymes latin, latin1, latin-1 ou iso8859-1, le jeu de caractère légèrement amélioré latin9 ou iso8859-15 et le jeu de caractère Windows cp1252 ou windows-1252, qui utilise une trentaine de caractères supplémentaires, la plupart typographiques. Voyez cette page pour les nuances.
Important ! Il est nécessaire de faire correspondre l'encodage du script, de la console et de la directive coding (voi point suivant).
L’encodage UTF-8
La norme Unicode a pour objectif de répertorier tous les caractères possibles de tous les alphabets, idéogrammes, symboles, ponctuations typographiques, dingbats et de plus en plus d’émoticones, en utilisant généralement l’UTF-8 qui code ces caractères en une suite d’un à quatre octets, un seul pour les caractères ASCII, deux pour chaque lettre accentuée et beaucoup d’alphabets non-européens, trois pour les idéogrammes, les symboles mathématiques et les signes typographiques spécialisés et quatre pour les émoticones, les hiéroglyphes et signes cunéiformes.
Il serait fastidieux de passer tous les cas en revue d’encodages croisés entre paramétrage de la console, directive d’encodage en début de script et type de sauvegarde, mais il est possible de relever deux incompatibilités habituelles :
- en console UTF-8, tentative d’imprimer un octet qui n’a pas de sens tout seul :
>>> print("\xe9") �
- A contrario, l’affichage d’un caractère codé sur deux octets dans une console en encodage latin donne :
print("é".decode("latin")) é
Conclusion : toujours bien veiller à ce que la directive d’encodage, la sauvegarde d’un script et le paramétrage de la console soient bien accordés.
Il était possible de préciser l’encodage UTF-8 en python2, à condition de sauvegarder le script et de paramétrer la console dans ce même encodage:
#! /usr/bin/python2.7 # -*- coding: utf-8 -*- print("Ça va plutôt bien")
Un script écrit en python2 sans encodage spécifié devrait toujours fonctionner en python3, puisque l’encodage ASCII est un sous-ensemble des autres encodages. En python3, l’encodage par défaut est l’UTF-8, il ne faut donc plus rien préciser, sauf si vous voulez sauvegarder vos script en iso8859-1 ou cp125 ou quelqu’autre encodage.
Note : la différence de poids de fichier texte en français entre les encodages latins (dont cp1252) et UTF-8 d’un même texte est assez faible, environ 6% de plus pour le second.
Codage des caractères en UTF pour python 2 et 3
Python2 code les caractères en octets :
#! /usr/bin/python2.7 # -*- coding: utf-8 -*- chaine ="ôté" print(len(chaine)) # la réponse est 5, 'ô' et 'é' valant chacun deux octets print(chaine[0]) # la réponse est '�', premier octet du 'ô', qui isolé n’a pas de sens en UTF-8
Python3 privilégie l’UTF-8 et a modifié manipulation des chaînes : pour la chaîne ch="ôté", ch[0] vaut le caractère ô. C’est extrêmement utile lors de décomposition de chaînes. En python3 :
#! /usr/bin/python3 chaine ="ôté" print(len(chaine)) # la réponse est 3, chaque caractère valant un print(chaine[0]) # la réponse est 'ô'
Note : afin de ne pas multiplier les cas, la suite ne considérera plus que l’encodage UTF-8. Dans les scripts en python2 uniquement, la première ligne (après l’éventuel #! /usr/bin/python2.7 ) devait être :
# -*- coding: utf-8 -*-
Afin de gérer l’Unicode, python2 avait recours aux méthodes encode() et decode(), qui devait préciser le type d’encodage. Dans une console en latin1 :
>>> # -*- coding: utf-8 -*- >>> ch="être" >>> uch =ch.decode("utf8") ; uch u'\xeatre' >>> lch =ch.decode("latin1") ; lch u'\xc3\xaatre'
En UTF-8, le "ê" est représenté par l’octet \xea, qui vaut 234, son rang unicode ; en latin1, ce caractère est représenté par \xc3\xaa, les deux octets qui forment le caractère.
La fonction unicode() fait exactement la même chose :
>>> unicode("être", "latin1") u'\xc3\xaatre'
encode() semblait poser un problème en python2 (cela a été discuté sur plusieurs forums) :
>>> "ê".decode("utf8") u'\xea' >>> "ê".decode("utf8").encode("utf8") '\xc3\xaa'
En python3, la méthode encode() transforme une chaîne en objet 'bytes' selon un type d’encodage :
>>> ch ="être" >>> ch.encode("latin1") b'\xeatre' >>> ch.encode("utf8") b'\xc3\xaatre' >>> str.encode(ch, "utf8") b'\xc3\xaatre'
où l’on voit que le "ê" est codé avec l’octet 0xea (234) en dans l’encodage ISO8859-1 et les deux octets 0xc3 (195) et 0xaa (170) en UTF-8.
Note : encode() étant une méthode pour chaînes, "chaine".encode(encodage) et str.encode("chaine", encodage) sont deux expressions équivalentes.
En python3, la méthode decode() transforme un objet "bytes" en chaine selon un type d’encodage :
>>> b"\xc3\xaatre".decode("utf-8") 'être' >>> b"\xc3\xaatre".decode("latin1") 'être'
La tentative de décoder avec ISO8859-1 un objet ’bytes’ issu d’un encodage UTF-8 produit ici deux caractères.
Note : decode() est une méthode pour les objets ’bytes’ :
>>> bytes.decode(b"\xe9", "latin") 'é'
Python3 dispose de la fonction ascii() qui transforme toute chaîne (ou tout objet python comme les dictionnaires, liste, etc.) en une représentation en ASCII :
>>> print(ascii("pµə")) "'p\xb5\u0259'"
- p est un caractère ascii, littéral
- µ est un caractère dont le point de code est inférieur à 256, et donc représenté par un octet (en hexadécimal)
- ə a un point de code supérieur à 255, il est représenté par en hexadécimal sur quatre chiffres, préfixé par u pour Unicode.
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 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"
Ce codage \#e0 a été retenu pour les octets-caractères non-ASCII du type bytes.
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"
Unicode
Python2 disposait d’un objet unicode et d’une fonction unicode(), et même si un caractère spécial était codé en plusieurs octets, sa longueur unicode valait 1 :
>>> u"œ"; type(u"œuf"); len(u"œuf") u'\u0153uf' <type 'unicode'> 3
Python3 a gardé l’écriture u"", qui est un équivalent à "" et est considéré en python3 comme un objet ’str’ :
>>> u"œ"; type(u"œuf"); len(u"œuf") 'œ' <class 'str'> 3
La fonction unicode(chaine, encodage) permettait de transformer une chaîne codée sur un octet en chaîne unicode :
>>> unicode(chr(164), "latin1") u'\xa4' >>> unicode(chr(164), "latin9") u'\u20ac'
0xa4 correspond au décimal 164 du codage des latin1 et cp1252 et au point de code 164 de l’Unicode, où se situe le caractère ¤ (currency sign)
0x20ac correspond au décimal 164 du codage latin9 (ou iso8819-15), mais au point de code 8364 de l’Unicode (0x20ac en hewadécimal)), où se situe le caractère € (euro sign)
En python3, unicode() n’existe plus, les chaînes étant nativement en utf-8. Il y est très simple d’utiliser ord() capable de situer tous les points «unicode» :
>>> ord("€") 8364
Python2 connaissait une fonction spéciale convertissant un nombre dépassant 255 en son caractère ’Unicode’ :
>>> unichr(0x20ac) €
Python3 a étendu la fonction chr() à tous les points unicode :
>>> chr(0x20ac) €
type() ne retourne plus le même «type»
En python2, type() appliqué à une valeur renvoyait :
>>> type(.977) <type 'float'>
En python3, le retour de la fonction type se libelle différemment :
>>> type(.977) <class 'float'>
Dans les deux cas, la réponse n’est pas une chaîne :
>>> type(type(.977)) <class 'type'>
...ce qui signifie que type() ==<class 'float'> ne peut fonctionner, il faut utiliser
>>> a="123" ; type(a) ==type(0) True >>> a=123 ; type(a) ==int True
Le type 'bytes' (octet)
En python2, type(bytes("é")) retournait <type 'str'>
En python3, type(bytes("é", "latin1")) retourne <class 'bytes'>
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 255e caractère Unicode, 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'!', octet 33) et le second est le caractère 7, soit l’octet 45.
Mais le plus simple est de constituer une liste pour ensuite la transformer en objet ’bytes’ :
>>> bytes([0, 255, 45, 95, 250])
.isalpha()
En python2,les lettres accentuées n’étaient pas considérées comme des caractères alphabétiques : print("é".isalpha()) renvoyait False
En python3 print("é".isalpha()) renvoie True
Listes
En python2, type([]) retournait <type 'list'>
type([]) retourne < class '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([]))
Attention : beaucoup de fonctions qui retournaient des listes en pythons renvoient maintenant des objets spécifiques
xrange() est supprimé
En python2 range() créait d’abord un objet de type 'list' (ce qui peut prendre un certain temps et encombrer la mémoire) et puis le parcourait.
for i in range(10**8) : print i
Pour éviter cela, xrange() générait les nombres un à un :
for i in xrange(10**8) : print i
En python3, range() a repris cette caractéristique et xrange() est supprimé. 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 :
>>> list(range(3, 10, 2)) [3, 5, 7, 9]
Certaines fonctions ne renvoient plus de liste
En python2, les fonctions filter(), map() et zip() renvoyaient des listes [] ou des chaînes "". Par exemple,
>>> filter(lambda x: 115 < ord(x) < 125, "python") 'yt' >>> map(ord, "python") [112, 121, 116, 104, 111, 110] >>> zip([1, 2, 3], ("a", "b", "c")) [(1, 'a'), (2, 'b'), (3, 'c')]
Les fonctions internes filter(), map() et zip() renvoient en python3 des objets de classes (ou types) 'filter', 'map'et 'zip', objets itérables que l’on peut transformer en chaîne ou en liste :
>>> "".join(filter(lambda x: 115 < ord(x) < 123, "python")) 'yt' >>> list(map(ord, "python")) [112, 121, 116, 104, 111, 110] >>> list(zip([1, 2, 3], ("a", "b", "c"))) [(1, 'a'), (2, 'b'), (3, 'c')]
Remarquons que ces filter() et map() peuvent être évités :
>>> "".join([x for x in "python" if 115 < ord(x) < 123]) 'yt' >>> [ord(x) for x in "python"] [112, 121, 116, 104, 111, 110]
Dictionnaires
En python2, type({}) retournait <type 'dict'>.
type({}) retourne < class 'dict'>
has_key() était utilisé en python2 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
En python2, les méthodes pour dictionnaires renvoyaient des listes :
>>> dic ={1:3, 2:4} ; dic.keys() ; dic.values() ; dic.items() [1, 2] [3, 4] [(1, 3), (2, 4)]
Les méthodes .keys(), .values() et .items() des { dictionnaires } renvoient en python3 maintenant des objets de classes (ou types) dict_keys, dict_values et dict_items, ce qui 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 cependant possible de transformer ces objets en listes :
dic ={1:3, 2:4} ; list(dic.keys()) ; type(dic.values()) ; dic.items() [1, 2] <class 'dict_values'> dict_items([(1, 3), (2, 4)])
En python2, il était possible de comparer des dictionnaires :
>>> a={5:7} ; b={8:9} >>> a > b ; a < b False True
Cela n’a plus de sens en python3 :
>>> a ={2:8} ; b={6:7} >>> a < b Traceback (most recent call last): File '<stdin>', line 1, in <module> TypeError: '<' not supported between instances of 'dict' and 'dict'
En python2, max() et min() retrouvaient toujours la clé maximale ou minimale d’un dictionnaire, avec la hiérarchie 0 < {} < [] < set() < "" < tuple().
En python3, max() et min() ne fonctionnent que si toutes les toutes les clés sont de même classe / type.
>>> max({1:2, "j":4}) Traceback (most recent call last): File '<stdin>', line 1, in <module> TypeError: '>' not supported between instances of 'str' and 'int'
Note : Python3 connaît une nouvelle forme de dictionnaire : OrderedDict, qui conserve la mémoire de l’ordre de ses éléments.
Ensembles
En python2, les ensembles étaient affichés set([1, 2, 3]) par la console :
>>> a ={1, 2, 3} ; a set([1, 2, 3])
En python3, les ensembles sont affichés {1, 2, 3} par la console :
>>> a ={1, 2, 3} ; a {1, 2, 3}
Cela ne pose pas trop de problèmes, car un fichier contenant set([1, 2, 3]) issu d’une sauvegarde en python2 peut être compris par eval() :
with open("svg.txt", "w") as fd : print >> fd, set([1,2,3])
with open("svg.txt") as fd : print(eval(fd.read()))
raise
Python2 permettait cette syntaxe :
>>> raise KeyboardInterrupt, "Commmentaire facultatif"
Python3 exige les parenthèses pour le commentaire :
>>> raise KeyboardInterrupt("§ Mon commentaire §") Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyboardInterrupt: § Mon commentaire §
Variables
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.
Comme print n’est plus un mot réservé en python3, il est possible de l’utiliser comme variable, ce qui empêche l’utilisation de la fonction print().