Python Language
Incompatibilités entre Python 2 et Python 3
Recherche…
Introduction
Contrairement à la plupart des langages, Python prend en charge deux versions principales. Depuis 2008, quand Python 3 est sorti, beaucoup ont fait la transition, alors que beaucoup ne l'ont pas fait. Afin de comprendre les deux, cette section couvre les différences importantes entre Python 2 et Python 3.
Remarques
Il existe actuellement deux versions prises en charge de Python: 2.7 (Python 2) et 3.6 (Python 3). De plus, les versions 3.3 et 3.4 reçoivent les mises à jour de sécurité au format source.
Python 2.7 est rétrocompatible avec la plupart des versions antérieures de Python et peut exécuter le code Python de la plupart des versions 1.x et 2.x de Python sans modification. Il est largement disponible, avec une vaste collection de paquets. Il est également considéré comme obsolète par les développeurs de CPython et ne reçoit que le développement de la sécurité et des correctifs. Les développeurs de CPython ont l’intention d’abandonner cette version du langage en 2020 .
Selon Python Enhancement Proposal 373, il n'y aura pas de versions futures planifiées de Python 2 après le 25 juin 2016, mais les corrections de bogues et les mises à jour de sécurité seront prises en charge jusqu'en 2020 (il ne spécifie pas la date exacte de 2020). 2.)
Python 3 a intentionnellement rompu la compatibilité avec les versions antérieures pour répondre aux préoccupations des développeurs de langage concernant le noyau du langage. Python 3 reçoit de nouveaux développements et de nouvelles fonctionnalités. C'est la version du langage que les développeurs de langue ont l'intention de faire évoluer.
Au fil du temps, entre la version initiale de Python 3.0 et la version actuelle, certaines fonctionnalités de Python 3 ont été transférées dans Python 2.6, et d'autres parties de Python 3 ont été étendues pour avoir une syntaxe compatible avec Python 2. Il est donc possible d'écrire Python qui fonctionnera sur Python 2 et Python 3, en utilisant les futures importations et les modules spéciaux (comme six ).
Les futures importations doivent être au début de votre module:
from __future__ import print_function
# other imports and instructions go after __future__
print('Hello world')
Pour plus d'informations sur le module __future__
, consultez la page correspondante dans la documentation Python .
L' outil 2to3 est un programme Python qui convertit le code Python 2.x en code Python 3.x. Voir aussi la documentation Python .
Le paquet six fournit des utilitaires pour la compatibilité avec Python 2/3:
- accès unifié aux bibliothèques renommées
- variables pour les types chaîne / unicode
- fonctions pour la méthode qui a été supprimée ou a été renommée
Une référence pour les différences entre Python 2 et Python 3 peut être trouvée ici .
Relevé d'impression ou fonction d'impression
Dans Python 2, print
est une déclaration:
print "Hello World"
print # print a newline
print "No newline", # add trailing comma to remove newline
print >>sys.stderr, "Error" # print to stderr
print("hello") # print "hello", since ("hello") == "hello"
print() # print an empty tuple "()"
print 1, 2, 3 # print space-separated arguments: "1 2 3"
print(1, 2, 3) # print tuple "(1, 2, 3)"
Dans Python 3, print()
est une fonction, avec des arguments de mots-clés pour des utilisations courantes:
print "Hello World" # SyntaxError
print("Hello World")
print() # print a newline (must use parentheses)
print("No newline", end="") # end specifies what to append (defaults to newline)
print("Error", file=sys.stderr) # file specifies the output buffer
print("Comma", "separated", "output", sep=",") # sep specifies the separator
print("A", "B", "C", sep="") # null string for sep: prints as ABC
print("Flush this", flush=True) # flush the output buffer, added in Python 3.3
print(1, 2, 3) # print space-separated arguments: "1 2 3"
print((1, 2, 3)) # print tuple "(1, 2, 3)"
La fonction d'impression a les paramètres suivants:
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
sep
est ce qui sépare les objets que vous passez à imprimer. Par exemple:
print('foo', 'bar', sep='~') # out: foo~bar
print('foo', 'bar', sep='.') # out: foo.bar
end
est la fin de l'instruction print. Par exemple:
print('foo', 'bar', end='!') # out: foo bar!
Impression à nouveau après une déclaration d'impression de fin de non-retour à la ligne imprimera à la même ligne:
print('foo', end='~')
print('bar')
# out: foo~bar
Remarque: Pour une compatibilité future, print
fonction d’ print
est également disponible dans Python 2.6 et supérieur . Cependant, il ne peut être utilisé que si l'analyse de l' instruction print
est désactivée avec
from __future__ import print_function
Cette fonction a exactement le même format que celle de Python 3, sauf qu’elle ne contient pas le paramètre flush
.
Voir PEP 3105 pour la justification.
Chaînes: Octets versus Unicode
En Python 2, il existe deux variantes de chaîne: celles faites d’octets de type ( str
) et celles de texte de type ( unicode
).
Dans Python 2, un objet de type str
est toujours une séquence d'octets, mais est couramment utilisé à la fois pour les données textuelles et binaires.
Un littéral de chaîne est interprété comme une chaîne d'octets.
s = 'Cafe' # type(s) == str
Il y a deux exceptions: Vous pouvez définir un littéral Unicode (text) explicitement en préfixant le littéral avec u
:
s = u'Café' # type(s) == unicode
b = 'Lorem ipsum' # type(b) == str
Vous pouvez également spécifier que les littéraux de chaîne d'un module entier doivent créer des littéraux Unicode (texte):
from __future__ import unicode_literals
s = 'Café' # type(s) == unicode
b = 'Lorem ipsum' # type(b) == unicode
Pour vérifier si votre variable est une chaîne (Unicode ou une chaîne d'octets), vous pouvez utiliser:
isinstance(s, basestring)
Dans Python 3, le type str
est un type de texte Unicode.
s = 'Cafe' # type(s) == str
s = 'Café' # type(s) == str (note the accented trailing e)
De plus, Python 3 a ajouté un objet bytes
, adapté aux "blobs" binaires ou à l'écriture dans des fichiers indépendants du codage. Pour créer un objet octets, vous pouvez ajouter un préfixe b
à un littéral de chaîne ou appeler la méthode d' encode
la chaîne:
# Or, if you really need a byte string:
s = b'Cafe' # type(s) == bytes
s = 'Café'.encode() # type(s) == bytes
Pour tester si une valeur est une chaîne, utilisez:
isinstance(s, str)
Il est également possible de préfixer des littéraux de chaîne avec un préfixe u
pour faciliter la compatibilité entre les bases de code Python 2 et Python 3. Puisque, dans Python 3, toutes les chaînes sont Unicode par défaut, ajouter une chaîne à un littéral avec u
n'a aucun effet:
u'Cafe' == 'Cafe'
Le préfixe de chaîne Unicode brut de Python 2, ur
n'est pas supporté, cependant:
>>> ur'Café'
File "<stdin>", line 1
ur'Café'
^
SyntaxError: invalid syntax
Notez que vous devez encode
un objet text ( str
) Python 3 pour le convertir en une représentation en bytes
de ce texte. Le codage par défaut de cette méthode est UTF-8 .
Vous pouvez utiliser le decode
pour demander à un objet bytes
quel texte Unicode il représente:
>>> b.decode()
'Café'
Bien que le type d' bytes
existe à la fois dans Python 2 et 3, le type unicode
n'existe que dans Python 2. Pour utiliser les chaînes Unicode implicites de Python 3 dans Python 2, ajoutez ce qui suit en haut de votre fichier de code:
from __future__ import unicode_literals
print(repr("hi"))
# u'hi'
Une autre différence importante est que l’indexation des octets dans Python 3 donne un résultat int
comme celui-ci:
b"abc"[0] == 97
Alors que le découpage en taille de 1 donne un objet de longueur 1 octet:
b"abc"[0:1] == b"a"
En outre, Python 3 corrige certains comportements inhabituels avec Unicode, à savoir l'inversion des chaînes d'octets dans Python 2. Par exemple, le problème suivant est résolu:
# -*- coding: utf8 -*-
print("Hi, my name is Łukasz Langa.")
print(u"Hi, my name is Łukasz Langa."[::-1])
print("Hi, my name is Łukasz Langa."[::-1])
# Output in Python 2
# Hi, my name is Łukasz Langa.
# .agnaL zsakuŁ si eman ym ,iH
# .agnaL zsaku�� si eman ym ,iH
# Output in Python 3
# Hi, my name is Łukasz Langa.
# .agnaL zsakuŁ si eman ym ,iH
# .agnaL zsakuŁ si eman ym ,iH
Division entière
Le symbole de division standard ( /
) fonctionne différemment dans Python 3 et Python 2 lorsqu'il est appliqué à des entiers.
Lors de la division d'un entier par un autre entier dans Python 3, l'opération de division x / y
représente une division réelle (utilise la méthode __truediv__
) et produit un résultat à virgule flottante. Pendant ce temps, la même opération dans Python 2 représente une division classique qui arrondit le résultat à l'infini négatif (également appelé prise de parole ).
Par exemple:
Code | Sortie Python 2 | Sortie Python 3 |
---|---|---|
3 / 2 | 1 | 1,5 |
2 / 3 | 0 | 0.6666666666666666 |
-3 / 2 | -2 | -1.5 |
Le comportement d'arrondi par rapport à zéro a été déprécié dans Python 2.2 , mais reste dans Python 2.7 pour des raisons de compatibilité ascendante et a été supprimé dans Python 3.
Note: Pour obtenir un résultat flottant dans Python 2 (sans arrondi), vous pouvez spécifier l'un des opérandes avec le point décimal. L'exemple ci-dessus de 2/3
qui donne 0
dans Python 2 doit être utilisé comme 2 / 3.0
ou 2.0 / 3
ou 2.0/3.0
pour obtenir 0.6666666666666666
Code | Sortie Python 2 | Sortie Python 3 |
---|---|---|
3.0 / 2.0 | 1,5 | 1,5 |
2 / 3.0 | 0.6666666666666666 | 0.6666666666666666 |
-3.0 / 2 | -1.5 | -1.5 |
Il y a aussi l' opérateur de division par étage ( //
), qui fonctionne de la même manière dans les deux versions: il est arrondi à l'entier le plus proche. (bien qu'un float soit retourné lorsqu'il est utilisé avec des floats) Dans les deux versions, l'opérateur //
correspond à __floordiv__
.
Code | Sortie Python 2 | Sortie Python 3 |
---|---|---|
3 // 2 | 1 | 1 |
2 // 3 | 0 | 0 |
-3 // 2 | -2 | -2 |
3.0 // 2.0 | 1.0 | 1.0 |
2.0 // 3 | 0.0 | 0.0 |
-3 // 2.0 | -2,0 | -2,0 |
On peut explicitement imposer une division réelle ou une division par étage en utilisant des fonctions natives dans le module operator
:
from operator import truediv, floordiv
assert truediv(10, 8) == 1.25 # equivalent to `/` in Python 3
assert floordiv(10, 8) == 1 # equivalent to `//`
Bien que claire et explicite, l'utilisation des fonctions de l'opérateur pour chaque division peut être fastidieuse. La modification du comportement de l'opérateur /
sera souvent préférée. Une pratique courante consiste à éliminer le comportement de division typique en ajoutant la from __future__ import division
comme première instruction de chaque module:
# needs to be the first statement in a module
from __future__ import division
Code | Sortie Python 2 | Sortie Python 3 |
---|---|---|
3 / 2 | 1,5 | 1,5 |
2 / 3 | 0.6666666666666666 | 0.6666666666666666 |
-3 / 2 | -1.5 | -1.5 |
from __future__ import division
garantit que l'opérateur /
représente la division vraie et uniquement dans les modules qui contiennent l'importation __future__
, il n'y a donc aucune raison impérieuse de ne pas l'activer dans tous les nouveaux modules.
Remarque : Certains autres langages de programmation arrondissent vers zéro (troncature) plutôt que vers l'infini négatif, contrairement à Python (dans ces langages -3 / 2 == -1
). Ce comportement peut créer une confusion lors du portage ou de la comparaison du code.
Note sur les opérandes float : En alternative à la from __future__ import division
, on pourrait utiliser le symbole de division habituel /
et s'assurer qu'au moins un des opérandes est un float: 3 / 2.0 == 1.5
. Cependant, cela peut être considéré comme une mauvaise pratique. Il est trop facile d’écrire average = sum(items) / len(items)
et oublier de lancer l’un des arguments pour flotter. De plus, de tels cas peuvent fréquemment échapper à la connaissance pendant le test, par exemple, si vous testez un tableau contenant des float
mais recevez un tableau de données int
en production. De plus, si le même code est utilisé dans Python 3, les programmes qui s'attendent à ce que 3 / 2 == 1
soit vrai ne fonctionneront pas correctement.
Voir PEP 238 pour une explication plus détaillée des raisons pour lesquelles l’opérateur de division a été modifié dans Python 3 et pourquoi l’ancienne division devrait être évitée.
Voir le sujet Mathématiques simples pour en savoir plus sur la division.
Réduire n'est plus un intégré
Dans Python 2, la reduce
est disponible soit en tant que fonction intégrée, soit à partir du paquet functools
(version 2.6 et functools
), tandis qu'en Python 3, la reduce
n'est disponible qu'à partir de functools
. Cependant, la syntaxe pour reduce
à la fois dans Python2 et Python3 est identique et est reduce(function_to_reduce, list_to_reduce)
.
Par exemple, considérons la réduction d'une liste à une valeur unique en divisant chacun des nombres adjacents. Ici, nous utilisons la fonction truediv
de la bibliothèque d' operator
.
Dans Python 2.x, c'est aussi simple que:
>>> my_list = [1, 2, 3, 4, 5]
>>> import operator
>>> reduce(operator.truediv, my_list)
0.008333333333333333
Dans Python 3.x, l'exemple devient un peu plus compliqué:
>>> my_list = [1, 2, 3, 4, 5]
>>> import operator, functools
>>> functools.reduce(operator.truediv, my_list)
0.008333333333333333
Nous pouvons également utiliser à from functools import reduce
pour éviter d’appeler reduce
avec le nom de l’espace de noms.
Différences entre les fonctions range et xrange
Dans Python 2, la fonction range
renvoie une liste, tandis que xrange
crée un objet xrange
spécial, qui est une séquence immuable, qui, contrairement à d'autres types de séquence intégrés, ne prend pas en charge les méthodes d' index
ni de count
:
print(range(1, 10))
# Out: [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(isinstance(range(1, 10), list))
# Out: True
print(xrange(1, 10))
# Out: xrange(1, 10)
print(isinstance(xrange(1, 10), xrange))
# Out: True
En Python 3, xrange
a été étendu à la range
séquence, ce qui crée ainsi maintenant une range
objet. Il n'y a pas de type de xrange
:
print(range(1, 10))
# Out: range(1, 10)
print(isinstance(range(1, 10), range))
# Out: True
# print(xrange(1, 10))
# The output will be:
#Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
#NameError: name 'xrange' is not defined
De plus, depuis Python 3.2, la range
prend également en charge le découpage, l’ index
et le count
:
print(range(1, 10)[3:7])
# Out: range(3, 7)
print(range(1, 10).count(5))
# Out: 1
print(range(1, 10).index(7))
# Out: 6
L'avantage d'utiliser un type de séquence spécial au lieu d'une liste est que l'interpréteur n'a pas besoin d'allouer de la mémoire pour une liste et de la remplir:
# range(10000000000000000)
# The output would be:
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# MemoryError
print(xrange(100000000000000000))
# Out: xrange(100000000000000000)
Comme ce dernier comportement est généralement souhaité, le premier a été supprimé dans Python 3. Si vous voulez toujours avoir une liste dans Python 3, vous pouvez simplement utiliser le constructeur list()
sur un objet de range
:
print(list(range(1, 10)))
# Out: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Compatibilité
Afin de maintenir la compatibilité entre les deux versions 2.x Python et Python 3.x, vous pouvez utiliser le builtins
module du package externe future
pour atteindre à la fois en avant et en arrière-compatiblité-compatiblité:
#forward-compatible
from builtins import range
for i in range(10**8):
pass
#backward-compatible
from past.builtins import xrange
for i in xrange(10**8):
pass
La range
de la future
bibliothèque prend en charge le découpage, l' index
et le count
dans toutes les versions de Python, tout comme la méthode intégrée sur Python 3.2+.
Déballer les Iterables
Dans Python 3, vous pouvez décompresser une itération sans connaître le nombre exact d'éléments, et même avoir une variable contenant la fin de l'itérable. Pour cela, vous fournissez une variable pouvant collecter une liste de valeurs. Cela se fait en plaçant un astérisque avant le nom. Par exemple, décompresser une list
:
first, second, *tail, last = [1, 2, 3, 4, 5]
print(first)
# Out: 1
print(second)
# Out: 2
print(tail)
# Out: [3, 4]
print(last)
# Out: 5
Remarque : Lorsque vous utilisez la syntaxe de la *variable
, la variable
sera toujours une liste, même si le type d’origine n’était pas une liste. Il peut contenir zéro ou plusieurs éléments en fonction du nombre d'éléments de la liste d'origine.
first, second, *tail, last = [1, 2, 3, 4]
print(tail)
# Out: [3]
first, second, *tail, last = [1, 2, 3]
print(tail)
# Out: []
print(last)
# Out: 3
De même, décompresser un str
:
begin, *tail = "Hello"
print(begin)
# Out: 'H'
print(tail)
# Out: ['e', 'l', 'l', 'o']
Exemple de déballage d'une date
; _
est utilisé dans cet exemple comme une variable jetable (nous nous intéressons uniquement à la valeur de l' year
):
person = ('John', 'Doe', (10, 16, 2016))
*_, (*_, year_of_birth) = person
print(year_of_birth)
# Out: 2016
Il convient de mentionner que, puisque *
mange un nombre variable d’éléments, vous ne pouvez pas avoir deux *
pour la même itération dans une affectation - il ne saurait pas combien d’éléments entrent dans la première décompression, et combien dans la seconde. :
*head, *tail = [1, 2]
# Out: SyntaxError: two starred expressions in assignment
Jusqu'à présent, nous avons discuté du déballage dans les missions. *
et **
ont été étendus dans Python 3.5 . Il est maintenant possible d'avoir plusieurs opérations de décompression dans une expression:
{*range(4), 4, *(5, 6, 7)}
# Out: {0, 1, 2, 3, 4, 5, 6, 7}
Il est également possible de décompresser une itération en arguments de fonction:
iterable = [1, 2, 3, 4, 5]
print(iterable)
# Out: [1, 2, 3, 4, 5]
print(*iterable)
# Out: 1 2 3 4 5
Le déballage d'un dictionnaire utilise deux étoiles adjacentes **
( PEP 448 ):
tail = {'y': 2, 'z': 3}
{'x': 1, **tail}
# Out: {'x': 1, 'y': 2, 'z': 3}
Cela permet à la fois de remplacer les anciennes valeurs et de fusionner les dictionnaires.
dict1 = {'x': 1, 'y': 1}
dict2 = {'y': 2, 'z': 3}
{**dict1, **dict2}
# Out: {'x': 1, 'y': 2, 'z': 3}
Python 3 a supprimé le déballage des tuple dans les fonctions. Par conséquent, ce qui suit ne fonctionne pas en Python 3
# Works in Python 2, but syntax error in Python 3:
map(lambda (x, y): x + y, zip(range(5), range(5)))
# Same is true for non-lambdas:
def example((x, y)):
pass
# Works in both Python 2 and Python 3:
map(lambda x: x[0] + x[1], zip(range(5), range(5)))
# And non-lambdas, too:
def working_example(x_y):
x, y = x_y
pass
Voir PEP 3113 pour une justification détaillée.
Relever et gérer les exceptions
Voici la syntaxe Python 2, notez les virgules ,
sur les lignes raise
et except
:
try:
raise IOError, "input/output error"
except IOError, exc:
print exc
En Python 3, la ,
la syntaxe est supprimée et remplacée par des parenthèses et as
mot - clé:
try:
raise IOError("input/output error")
except IOError as exc:
print(exc)
Pour assurer la compatibilité avec les versions antérieures, la syntaxe Python 3 est également disponible dans Python 2.6 et doit donc être utilisée pour tout nouveau code qui ne doit pas nécessairement être compatible avec les versions précédentes.
Python 3 ajoute également le chaînage des exceptions , dans lequel vous pouvez signaler qu'une autre exception est à l' origine de cette exception. Par exemple
try:
file = open('database.db')
except FileNotFoundError as e:
raise DatabaseError('Cannot open {}') from e
L'exception déclenchée dans l'instruction except
est de type DatabaseError
, mais l'exception d'origine est marquée comme attribut __cause__
de cette exception. Lorsque la traceback est affichée, l'exception d'origine sera également affichée dans la traceback:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
FileNotFoundError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
DatabaseError('Cannot open database.db')
Si vous lancez un bloc except
sans chaînage explicite:
try:
file = open('database.db')
except FileNotFoundError as e:
raise DatabaseError('Cannot open {}')
La traceback est
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
FileNotFoundError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
DatabaseError('Cannot open database.db')
Ni l'un ni l'autre n'est pris en charge dans Python 2.x; l'exception d'origine et sa traceback seront perdues si une autre exception est déclenchée dans le bloc sauf. Le code suivant peut être utilisé pour la compatibilité:
import sys
import traceback
try:
funcWithError()
except:
sys_vers = getattr(sys, 'version_info', (0,))
if sys_vers < (3, 0):
traceback.print_exc()
raise Exception("new exception")
Pour "oublier" l'exception précédemment levée, utilisez raise from None
try:
file = open('database.db')
except FileNotFoundError as e:
raise DatabaseError('Cannot open {}') from None
Maintenant, le traceur serait simplement
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
DatabaseError('Cannot open database.db')
Ou pour le rendre compatible avec Python 2 et 3, vous pouvez utiliser les six paquets comme suit:
import six
try:
file = open('database.db')
except FileNotFoundError as e:
six.raise_from(DatabaseError('Cannot open {}'), None)
Méthode .next () sur les itérateurs renommés
Dans Python 2, un itérateur peut être parcouru en utilisant une méthode appelée next
sur l'itérateur lui-même:
g = (i for i in range(0, 3))
g.next() # Yields 0
g.next() # Yields 1
g.next() # Yields 2
Dans Python 3, la méthode .next
a été renommée en .__next__
, reconnaissant son rôle «magic», l'appel de .next
déclenchera donc une .next
AttributeError
. La manière correcte d’accéder à cette fonctionnalité dans Python 2 et Python 3 consiste à appeler la fonction next
avec l’itérateur comme argument.
g = (i for i in range(0, 3))
next(g) # Yields 0
next(g) # Yields 1
next(g) # Yields 2
Ce code est portable entre les versions 2.6 et actuelles.
Comparaison de différents types
Des objets de différents types peuvent être comparés. Les résultats sont arbitraires mais cohérents. Ils sont classés de manière à ce que None
soit inférieur à tout, les types numériques sont plus petits que les types non numériques et tout le reste est ordonné par type lexicographiquement. Ainsi, un int
est inférieur à un str
et un tuple
est supérieur à une list
:
[1, 2] > 'foo'
# Out: False
(1, 2) > 'foo'
# Out: True
[1, 2] > (1, 2)
# Out: False
100 < [1, 'x'] < 'xyz' < (1, 'x')
# Out: True
Cela a été fait à l'origine afin qu'une liste de types mixtes puisse être triée et que les objets soient regroupés par type:
l = [7, 'x', (1, 2), [5, 6], 5, 8.0, 'y', 1.2, [7, 8], 'z']
sorted(l)
# Out: [1.2, 5, 7, 8.0, [5, 6], [7, 8], 'x', 'y', 'z', (1, 2)]
Une exception est déclenchée lors de la comparaison de différents types (non numériques):
1 < 1.5
# Out: True
[1, 2] > 'foo'
# TypeError: unorderable types: list() > str()
(1, 2) > 'foo'
# TypeError: unorderable types: tuple() > str()
[1, 2] > (1, 2)
# TypeError: unorderable types: list() > tuple()
Pour trier les listes mixtes dans Python 3 par types et pour assurer la compatibilité entre les versions, vous devez fournir une clé pour la fonction triée:
>>> list = [1, 'hello', [3, 4], {'python': 2}, 'stackoverflow', 8, {'python': 3}, [5, 6]]
>>> sorted(list, key=str)
# Out: [1, 8, [3, 4], [5, 6], 'hello', 'stackoverflow', {'python': 2}, {'python': 3}]
L'utilisation de str
comme fonction key
convertit temporairement chaque élément en chaîne uniquement à des fins de comparaison. Il voit ensuite la représentation des chaînes commençant par [
, '
, {
ou 0-9
et il est capable de les trier (et tous les caractères suivants).
Entrée utilisateur
En Python 2, les entrées utilisateur sont acceptées en utilisant la fonction raw_input
,
user_input = raw_input()
En Python 3, l'entrée utilisateur est acceptée à l'aide de la fonction d' input
.
user_input = input()
En Python 2, l' input
fonction accepte l' entrée et l' interpréter. Bien que cela puisse être utile, il a plusieurs considérations de sécurité et a été supprimé dans Python 3. Pour accéder à la même fonctionnalité, eval(input())
peut être utilisé.
Pour garder un script portable entre les deux versions, vous pouvez placer le code ci-dessous en haut de votre script Python:
try:
input = raw_input
except NameError:
pass
Changement de méthode de dictionnaire
Dans Python 3, de nombreuses méthodes de dictionnaire ont un comportement très différent de celui de Python 2, et beaucoup ont été supprimées également: has_key
, iter*
et view*
ont disparu. Au lieu de d.has_key(key)
, longtemps obsolète, il faut maintenant utiliser key in d
.
Dans Python 2, les keys
méthodes de dictionnaire, les values
et les items
renvoient des listes. Dans Python 3, ils renvoient des objets de vue à la place; les objets de vue ne sont pas des itérateurs et ils en diffèrent de deux manières, à savoir:
- ils ont la taille (on peut utiliser la fonction
len
sur eux) - ils peuvent être répétés plusieurs fois
De plus, comme avec les itérateurs, les modifications apportées au dictionnaire sont reflétées dans les objets de vue.
Python 2.7 a rétrogradé ces méthodes depuis Python 3; Ils sont disponibles sous viewkeys
de viewkeys
, de viewvalues
et de viewitems
. Pour transformer le code Python 2 en code Python 3, les formulaires correspondants sont les suivants:
-
d.keys()
,d.values()
etd.items()
de Python 2 doivent être remplacés parlist(d.keys())
,list(d.values())
etlist(d.items())
-
d.iterkeys()
,d.itervalues()
etd.iteritems()
doivent être changés eniter(d.keys())
, ou même mieux,iter(d)
;iter(d.values())
etiter(d.items())
respectivement - et enfin les méthodes
d.viewkeys()
,d.viewvalues()
etd.viewitems()
peuvent être remplacées pard.keys()
,d.values()
etd.items()
.
Le portage du code Python 2 qui itère sur les clés, les valeurs ou les éléments du dictionnaire tout en le mutant est parfois délicat. Considérer:
d = {'a': 0, 'b': 1, 'c': 2, '!': 3}
for key in d.keys():
if key.isalpha():
del d[key]
Le code semble fonctionner de la même manière dans Python 3, mais la méthode keys
renvoie un objet de vue, pas une liste, et si le dictionnaire change de taille en itération, le code Python 3 se bloquera avec RuntimeError: dictionary changed size during iteration
. La solution est bien sûr d'écrire correctement for key in list(d)
.
De même, les objets de vue se comportent différemment des itérateurs: on ne peut pas utiliser next()
et on ne peut pas reprendre l' itération; au lieu de cela redémarrer; Si le code Python 2 transmet la valeur de retour de d.iterkeys()
, d.itervalues()
ou d.iteritems()
à une méthode qui attend un itérateur au lieu d'une itération , cela devrait être iter(d)
, iter(d.values())
ou iter(d.items())
dans Python 3.
instruction exec est une fonction dans Python 3
Dans Python 2, exec
est une instruction, avec une syntaxe spéciale: exec code [in globals[, locals]].
Dans Python 3, exec
est maintenant une fonction: exec(code, [, globals[, locals]])
, et la syntaxe Python 2 SyntaxError
une SyntaxError
.
Au fur et à mesure que print
passait d'une instruction à une fonction, une importation __future__
était également ajoutée. Cependant, il n'y a pas from __future__ import exec_function
, car il n'est pas nécessaire: l'instruction exec dans Python 2 peut également être utilisée avec une syntaxe qui ressemble exactement à l' exec
fonction exec
dans Python 3. Vous pouvez donc modifier les instructions
exec 'code'
exec 'code' in global_vars
exec 'code' in global_vars, local_vars
aux formes
exec('code')
exec('code', global_vars)
exec('code', global_vars, local_vars)
et ces dernières formes sont garanties de fonctionner de manière identique dans Python 2 et Python 3.
bug de la fonction hasattr dans Python 2
Dans Python 2, lorsqu'une propriété hasattr
une erreur, hasattr
ignore cette propriété et renvoie False
.
class A(object):
@property
def get(self):
raise IOError
class B(object):
@property
def get(self):
return 'get in b'
a = A()
b = B()
print 'a hasattr get: ', hasattr(a, 'get')
# output False in Python 2 (fixed, True in Python 3)
print 'b hasattr get', hasattr(b, 'get')
# output True in Python 2 and Python 3
Ce bug est corrigé dans Python3. Donc, si vous utilisez Python 2, utilisez
try:
a.get
except AttributeError:
print("no get property!")
ou utilisez plutôt getattr
p = getattr(a, "get", None)
if p is not None:
print(p)
else:
print("no get property!")
Modules renommés
Quelques modules de la bibliothèque standard ont été renommés:
Ancien nom | Nouveau nom |
---|---|
_winreg | winreg |
ConfigParser | configparser |
copy_reg | copyreg |
Queue | queue |
SocketServer | socketserver |
_markupbase | markupbase |
repr | reproche |
test.test_support | test.support |
Tkinter | tkinter |
tkFileDialog | tkinter.filedialog |
urllib / urllib2 | urllib, urllib.parse, urllib.error, urllib.response, urllib.request, urllib.robotparser |
Certains modules ont même été convertis de fichiers en bibliothèques. Prenons l'exemple de tkinter et urllib ci-dessus.
Compatibilité
Lorsque vous maintenez la compatibilité entre les versions Python 2.x et 3.x, vous pouvez utiliser le future
package externe pour importer des packages de bibliothèque standard de niveau supérieur avec des noms Python 3.x sur les versions Python 2.x.
Constantes Octales
Dans Python 2, un littéral octal pourrait être défini comme
>>> 0755 # only Python 2
Pour assurer la compatibilité croisée, utilisez
0o755 # both Python 2 and Python 3
Toutes les classes sont des "nouvelles classes" dans Python 3.
Dans Python 3.x
toutes les classes sont des classes de nouveau style ; lors de la définition d'une nouvelle classe, python le fait hériter implicitement de l' object
. En tant que tel, la spécification d’un object
dans une définition de class
est une option complètement facultative:
class X: pass
class Y(object): pass
Ces deux classes contiennent désormais des object
dans leur mro
(ordre de résolution de la méthode):
>>> X.__mro__
(__main__.X, object)
>>> Y.__mro__
(__main__.Y, object)
Dans Python 2.x
classes sont, par défaut, d'anciennes classes; ils n'héritent pas implicitement d' object
. Cela fait que la sémantique des classes diffère selon que l'on ajoute explicitement un object
tant que class
base:
class X: pass
class Y(object): pass
Dans ce cas, si nous essayons d'imprimer le __mro__
de Y
, une sortie similaire à celle du cas Python 3.x
apparaîtra:
>>> Y.__mro__
(<class '__main__.Y'>, <type 'object'>)
Cela se produit parce que nous avons explicitement fait hériter Y
de l’objet lors de sa définition: class Y(object): pass
. Pour la classe X
qui n'hérite pas d'objet, l'attribut __mro__
n'existe pas, en essayant d'y accéder aboutit à une erreur AttributeError
.
Afin de garantir la compatibilité entre les deux versions de Python, les classes peuvent être définies avec un object
comme classe de base:
class mycls(object):
"""I am fully compatible with Python 2/3"""
Alternativement, si la variable __metaclass__
est définie pour type
une portée globale, toutes les classes définies ultérieurement dans un module donné sont implicitement new-style sans avoir à hériter explicitement de l' object
:
__metaclass__ = type
class mycls:
"""I am also fully compatible with Python 2/3"""
Suppression des opérateurs <> et ``, synonyme de! = Et repr ()
Dans Python 2, <>
est un synonyme de !=
; de même, `foo`
est un synonyme de repr(foo)
.
>>> 1 <> 2
True
>>> 1 <> 1
False
>>> foo = 'hello world'
>>> repr(foo)
"'hello world'"
>>> `foo`
"'hello world'"
>>> 1 <> 2
File "<stdin>", line 1
1 <> 2
^
SyntaxError: invalid syntax
>>> `foo`
File "<stdin>", line 1
`foo`
^
SyntaxError: invalid syntax
encoder / décoder en hexadécimal n'est plus disponible
"1deadbeef3".decode('hex')
# Out: '\x1d\xea\xdb\xee\xf3'
'\x1d\xea\xdb\xee\xf3'.encode('hex')
# Out: 1deadbeef3
"1deadbeef3".decode('hex')
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AttributeError: 'str' object has no attribute 'decode'
b"1deadbeef3".decode('hex')
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# LookupError: 'hex' is not a text encoding; use codecs.decode() to handle arbitrary codecs
'\x1d\xea\xdb\xee\xf3'.encode('hex')
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# LookupError: 'hex' is not a text encoding; use codecs.encode() to handle arbitrary codecs
b'\x1d\xea\xdb\xee\xf3'.encode('hex')
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AttributeError: 'bytes' object has no attribute 'encode'
Cependant, comme suggéré par le message d'erreur, vous pouvez utiliser le module de codecs
pour obtenir le même résultat:
import codecs
codecs.decode('1deadbeef4', 'hex')
# Out: b'\x1d\xea\xdb\xee\xf4'
codecs.encode(b'\x1d\xea\xdb\xee\xf4', 'hex')
# Out: b'1deadbeef4'
Notez que codecs.encode
renvoie un objet bytes
. Pour obtenir un objet str
, il suffit de decode
en ASCII:
codecs.encode(b'\x1d\xea\xdb\xee\xff', 'hex').decode('ascii')
# Out: '1deadbeeff'
Fonction cmp supprimée dans Python 3
Dans Python 3, la fonction intégrée cmp
été supprimée, avec la méthode spéciale __cmp__
.
De la documentation:
La fonction
cmp()
doit être considérée comme disparue et la méthode spéciale__cmp__()
n'est plus prise en charge. Utilisez__lt__()
pour le tri,__eq__()
avec__hash__()
, et d'autres comparaisons riches si nécessaire. (Si vous avez vraiment besoin de la fonctionnalitécmp()
, vous pouvez utiliser l'expression(a > b) - (a < b)
comme équivalent pourcmp(a, b)
.)
De plus, toutes les fonctions intégrées acceptant le paramètre cmp
acceptent désormais uniquement le paramètre key
mot clé uniquement.
Dans le module functools
il y a aussi une fonction utile cmp_to_key(func)
qui vous permet de convertir une fonction de style cmp
une fonction de style key
:
Transformer une ancienne fonction de comparaison en une fonction clé. Utilisé avec les outils qui acceptent les fonctions clés (telles que
sorted()
,min()
,max()
,heapq.nlargest()
,heapq.nsmallest()
,itertools.groupby()
). Cette fonction est principalement utilisée comme outil de transition pour les programmes en cours de conversion à partir de Python 2 qui prend en charge l’utilisation de fonctions de comparaison.
Variables fuites dans la compréhension de la liste
x = 'hello world!'
vowels = [x for x in 'AEIOU']
print (vowels)
# Out: ['A', 'E', 'I', 'O', 'U']
print(x)
# Out: 'U'
x = 'hello world!'
vowels = [x for x in 'AEIOU']
print (vowels)
# Out: ['A', 'E', 'I', 'O', 'U']
print(x)
# Out: 'hello world!'
Comme on peut le voir sur l'exemple, dans Python 2, la valeur de x
été divulguée: cela masquait hello world!
et imprimé U
, puisque c'était la dernière valeur de x
à la fin de la boucle.
Cependant, en Python 3 x
imprime le monde original défini à l'origine hello world!
, puisque la variable locale de la compréhension de la liste ne masque pas les variables de la portée environnante.
De plus, ni les expressions génératrices (disponibles dans Python depuis la version 2.5), ni les interprétations de dictionnaire ou de jeu (qui ont été renvoyées vers Python 2.7 à partir de Python 3), ne contenaient de variables dans Python 2.
Notez que dans Python 2 et Python 3, les variables entrent dans la portée environnante lors de l'utilisation d'une boucle for:
x = 'hello world!'
vowels = []
for x in 'AEIOU':
vowels.append(x)
print(x)
# Out: 'U'
carte()
map()
est une méthode intégrée utile pour appliquer une fonction aux éléments d'une itération. Dans Python 2, map
renvoie une liste. Dans Python 3, map
renvoie un objet map , qui est un générateur.
# Python 2.X
>>> map(str, [1, 2, 3, 4, 5])
['1', '2', '3', '4', '5']
>>> type(_)
>>> <class 'list'>
# Python 3.X
>>> map(str, [1, 2, 3, 4, 5])
<map object at 0x*>
>>> type(_)
<class 'map'>
# We need to apply map again because we "consumed" the previous map....
>>> map(str, [1, 2, 3, 4, 5])
>>> list(_)
['1', '2', '3', '4', '5']
Dans Python 2, vous pouvez transmettre None
pour servir de fonction d'identité. Cela ne fonctionne plus dans Python 3.
>>> map(None, [0, 1, 2, 3, 0, 4])
[0, 1, 2, 3, 0, 4]
>>> list(map(None, [0, 1, 2, 3, 0, 5]))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
De plus, lors du passage de plusieurs arguments itérables en tant que arguments dans Python 2, les plaquettes de map
les itérables les plus courtes avec None
(similaire à itertools.izip_longest
). Dans Python 3, l'itération s'arrête après la plus courte itération.
En Python 2:
>>> map(None, [1, 2, 3], [1, 2], [1, 2, 3, 4, 5])
[(1, 1, 1), (2, 2, 2), (3, None, 3), (None, None, 4), (None, None, 5)]
En Python 3:
>>> list(map(lambda x, y, z: (x, y, z), [1, 2, 3], [1, 2], [1, 2, 3, 4, 5]))
[(1, 1, 1), (2, 2, 2)]
# to obtain the same padding as in Python 2 use zip_longest from itertools
>>> import itertools
>>> list(itertools.zip_longest([1, 2, 3], [1, 2], [1, 2, 3, 4, 5]))
[(1, 1, 1), (2, 2, 2), (3, None, 3), (None, None, 4), (None, None, 5)]
Note : au lieu de map
utilisez des listes compréhensibles compatibles avec Python 2/3. Remplacement de la map(str, [1, 2, 3, 4, 5])
:
>>> [str(i) for i in [1, 2, 3, 4, 5]]
['1', '2', '3', '4', '5']
filter (), map () et zip () renvoient des itérateurs au lieu de séquences
Dans le filter
Python 2, les fonctions intégrées map
et zip
renvoient une séquence. map
et zip
renvoient toujours une liste alors qu'avec filter
le type de retour dépend du type de paramètre donné:
>>> s = filter(lambda x: x.isalpha(), 'a1b2c3')
>>> s
'abc'
>>> s = map(lambda x: x * x, [0, 1, 2])
>>> s
[0, 1, 4]
>>> s = zip([0, 1, 2], [3, 4, 5])
>>> s
[(0, 3), (1, 4), (2, 5)]
Dans Python 3 filter
, map
et zip
return iterator à la place:
>>> it = filter(lambda x: x.isalpha(), 'a1b2c3')
>>> it
<filter object at 0x00000098A55C2518>
>>> ''.join(it)
'abc'
>>> it = map(lambda x: x * x, [0, 1, 2])
>>> it
<map object at 0x000000E0763C2D30>
>>> list(it)
[0, 1, 4]
>>> it = zip([0, 1, 2], [3, 4, 5])
>>> it
<zip object at 0x000000E0763C52C8>
>>> list(it)
[(0, 3), (1, 4), (2, 5)]
Puisque Python 2 itertools.izip
est équivalent à Python 3, zip
izip
a été supprimé sur Python 3.
Importations absolues / relatives
Dans Python 3, PEP 404 modifie la façon dont les importations fonctionnent à partir de Python 2. Les importations relatives implicites ne sont plus autorisées dans les packages et à from ... import *
importations ne sont autorisées que dans le code de niveau module.
Pour obtenir le comportement de Python 3 dans Python 2:
- la fonctionnalité d' importation absolue peut être activée à
from __future__ import absolute_import
- les importations relatives explicites sont encouragées à la place des importations implicites relatives
Pour plus de clarté, dans Python 2, un module peut importer le contenu d'un autre module situé dans le même répertoire, comme suit:
import foo
Notez que l'emplacement de foo
est ambigu à partir de la seule déclaration d'importation. Ce type d'importation implicite relative est donc déconseillé en faveur d' importations relatives explicites , qui ressemblent à ce qui suit:
from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo
from ...package import bar
from ...sys import path
Le point .
permet une déclaration explicite de l'emplacement du module dans l'arborescence.
Plus sur les importations relatives
Considérons un paquet défini par l'utilisateur appelé shapes
. La structure du répertoire est la suivante:
shapes
├── __init__.py
|
├── circle.py
|
├── square.py
|
└── triangle.py
circle.py
, square.py
et triangle.py
importent tous util.py
tant que module. Comment vont-ils se référer à un module du même niveau?
from . import util # use util.PI, util.sq(x), etc
OU
from .util import * #use PI, sq(x), etc to call functions
Le .
est utilisé pour les importations relatives de même niveau.
Considérons maintenant une autre disposition du module de shapes
:
shapes
├── __init__.py
|
├── circle
│ ├── __init__.py
│ └── circle.py
|
├── square
│ ├── __init__.py
│ └── square.py
|
├── triangle
│ ├── __init__.py
│ ├── triangle.py
|
└── util.py
Maintenant, comment ces 3 classes se réfèrent-elles à util.py?
from .. import util # use util.PI, util.sq(x), etc
OU
from ..util import * # use PI, sq(x), etc to call functions
Le ..
est utilisé pour les importations relatives au niveau parent. Ajouter plus .
s avec nombre de niveaux entre le parent et l'enfant.
Fichier I / O
file
n'est plus un nom intégré dans 3.x ( open
works).
Les détails internes des E / S de fichiers ont été déplacés vers le module io
standard de la bibliothèque, qui est également la nouvelle StringIO
de StringIO
:
import io
assert io.open is open # the builtin is an alias
buffer = io.StringIO()
buffer.write('hello, ') # returns number of characters written
buffer.write('world!\n')
buffer.getvalue() # 'hello, world!\n'
Le mode fichier (text vs binary) détermine maintenant le type de données produit en lisant un fichier (et le type requis pour l'écriture):
with open('data.txt') as f:
first_line = next(f)
assert type(first_line) is str
with open('data.bin', 'rb') as f:
first_kb = f.read(1024)
assert type(first_kb) is bytes
Le codage des fichiers texte est locale.getpreferredencoding(False)
par défaut sur tout ce qui est renvoyé par locale.getpreferredencoding(False)
. Pour spécifier explicitement un codage, utilisez le paramètre de mot-clé encoding
:
with open('old_japanese_poetry.txt', 'shift_jis') as text:
haiku = text.read()
La fonction round () et le type de retour
bris de cravate
En Python 2, l'utilisation de round()
sur un nombre proche de deux nombres entiers renvoie la valeur la plus éloignée de 0. Par exemple:
round(1.5) # Out: 2.0
round(0.5) # Out: 1.0
round(-0.5) # Out: -1.0
round(-1.5) # Out: -2.0
Dans Python 3 cependant, round()
renverra l'entier pair ( l'arrondi des banquiers ). Par exemple:
round(1.5) # Out: 2
round(0.5) # Out: 0
round(-0.5) # Out: 0
round(-1.5) # Out: -2
La fonction round () suit la stratégie d' arrondissement de moitié à pair qui arrondira les nombres à mi-chemin au nombre entier pair le plus proche (par exemple, round(2.5)
renvoie désormais 2 plutôt que 3.0).
Selon la référence dans Wikipedia , ceci est également connu sous le nom d' arrondi sans biais, d'arrondi convergent, d'arrondi de statisticien, d'arrondissement hollandais, d'arrondi gaussien ou d' arrondi impair .
La moitié à l'arrondissement fait partie de la norme IEEE 754 et c'est aussi le mode d'arrondi par défaut dans Microsoft .NET.
Cette stratégie d'arrondissement tend à réduire l'erreur d'arrondi totale. Étant donné qu'en moyenne, le nombre de chiffres arrondis est le même que le nombre de chiffres arrondis, les erreurs d'arrondi sont annulées. D'autres méthodes d'arrondi ont plutôt tendance à avoir un biais à la hausse ou à la baisse dans l'erreur moyenne.
type de retour round ()
La fonction round()
renvoie un type float
dans Python 2.7
round(4.8)
# 5.0
À partir de Python 3.0, si le second argument (nombre de chiffres) est omis, il renvoie un int
.
round(4.8)
# 5
Vrai, Faux et Aucun
Dans Python 2, True
, False
et None
sont des constantes intégrées. Ce qui signifie qu'il est possible de les réaffecter.
True, False = False, True
True # False
False # True
Vous ne pouvez pas faire cela avec None
depuis Python 2.4.
None = None # SyntaxError: cannot assign to None
Dans Python 3, True
, False
et None
sont maintenant des mots-clés.
True, False = False, True # SyntaxError: can't assign to keyword
None = None # SyntaxError: can't assign to keyword
Renvoie la valeur lors de l'écriture dans un objet fichier
En Python 2, l'écriture directe dans un descripteur de fichier renvoie None
:
hi = sys.stdout.write('hello world\n')
# Out: hello world
type(hi)
# Out: <type 'NoneType'>
Dans Python 3, l’écriture dans un handle renvoie le nombre de caractères écrits lors de l’écriture du texte et le nombre d’octets écrits lors de l’écriture des octets:
import sys
char_count = sys.stdout.write('hello world 🐍\n')
# Out: hello world 🐍
char_count
# Out: 14
byte_count = sys.stdout.buffer.write(b'hello world \xf0\x9f\x90\x8d\n')
# Out: hello world 🐍
byte_count
# Out: 17
long vs int
Dans Python 2, tout entier supérieur à C ssize_t
serait converti en type de données long
, indiqué par un suffixe L
sur le littéral. Par exemple, sur une version 32 bits de Python:
>>> 2**31
2147483648L
>>> type(2**31)
<type 'long'>
>>> 2**30
1073741824
>>> type(2**30)
<type 'int'>
>>> 2**31 - 1 # 2**31 is long and long - int is long
2147483647L
Cependant, dans Python 3, le type de données long
été supprimé. peu importe la taille de l'entier, ce sera un int
.
2**1024
# Output: 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
print(-(2**1024))
# Output: -179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
type(2**1024)
# Output: <class 'int'>
Valeur booléenne de classe
Dans Python 2, si vous souhaitez définir vous-même une valeur booléenne de classe, vous devez implémenter la méthode __nonzero__
dans votre classe. La valeur est True par défaut.
class MyClass:
def __nonzero__(self):
return False
my_instance = MyClass()
print bool(MyClass) # True
print bool(my_instance) # False
En Python 3, __bool__
est utilisé à la place de __nonzero__
class MyClass:
def __bool__(self):
return False
my_instance = MyClass()
print(bool(MyClass)) # True
print(bool(my_instance)) # False