Les principales différences entre Python 2.7.x et Python 3.x avec des exemples

De nombreux utilisateurs débutants de Python se demandent avec quelle version de Python ils doivent commencer. Ma réponse à cette question est généralement quelque chose du genre « allez-y avec la version dans laquelle votre tutoriel préféré a été écrit, et vérifiez les différences plus tard. »

Mais qu’en est-il si vous commencez un nouveau projet et que vous avez le choix ? Je dirais qu’il n’y a actuellement pas de « bon » ou de « mauvais » tant que Python 2.7.x et Python 3.x supportent tous deux les bibliothèques que vous prévoyez d’utiliser. Cependant, il vaut la peine de jeter un coup d’œil aux principales différences entre ces deux versions les plus populaires de Python afin d’éviter les pièges courants lors de l’écriture du code pour l’une ou l’autre, ou si vous envisagez de porter votre projet.

Sections

  • Sections
  • La __future__ module
  • La fonction print
    • Python 2
    • Python 3
  • Division d’entiers.
    • Python 2
    • Python 3
  • Unicode
    • Python 2
    • Python 3
  • Xrange
    • Python 2
    • Python 3
    • Le __contains__ méthode pour les objets range en Python 3
      • Note sur les différences de vitesse en Python 2 et 3
  • La levée d’exceptions.
    • Python 2
    • Python 3
  • Gestion des exceptions
    • Python 2
    • Python 3
    • Python 2
    • Python 3
  • For-variables de boucle et la fuite de l’espace de nom global
    • Python 2
    • Python 3
  • Comparaison des types non ordonnables
      .

    • Python 2
    • Python 3
  • Traitement des entrées utilisateur via input()
    • Python 2
    • Python 3
  • . Retourner des objets itérables au lieu de listes
    • Python 2
    • Python 3
  • Arrondi du banquier
    • Python. 2
    • Python 3
  • Plus d’articles sur Python 2 et Python 3

Le module __future__

Python 3.x a introduit certains mots-clés et fonctionnalités incompatibles avec Python 2 qui peuvent être importés via le module __future__ intégré dans Python 2. Il est recommandé d’utiliser les importations __future__ si vous prévoyez le support de Python 3.x pour votre code. Par exemple, si nous voulons que le comportement de division d’entiers de Python 3.x le comportement de division des entiers dans Python 2, nous pouvons l’importer via

from __future__ import division

Les autres fonctionnalités qui peuvent être importées du module __future__ sont listées dans le tableau ci-dessous :

feature optionnelle dans obligatoire dans effet
nested_scopes 2.1.0b1 2.2 PEP 227:Statically Nested Scopes
Generators 2.2.0a1 2.3 PEP 255:Simple Generators
Division 2.2.0a2 3.0 PEP 238:Changement de l’opérateur de division
absolu_import 2.5.0a1 3.0 PEP 328:Importations : Multi-lignes et absolus/relatifs with_statement 2.5.0a1 2.6 PEP 343:L’instruction « with » print_function 2.6.0a2 3.0 PEP 3105:Faire de print une fonction unicode_literals 2.6.0a2 3.0 PEP 3112:Bytes literals in Python 3000

(Source : (https://docs.python.org/2/library/__future__.html#module-__future

from platform import python_version

La fonction print

Très triviale, et le changement de la syntaxe print est probablement le changement le plus connu, mais il mérite tout de même d’être mentionné : L’instruction print de Python 2 a été remplacée par la fonction print(), ce qui signifie que nous devons envelopper l’objet que nous voulons imprimer dans des paranthèses.

Python 2 n’a pas de problème avec les paranthèses supplémentaires, mais en revanche, Python 3 soulèverait un SyntaxError si nous appelions la fonction print à la manière de Python 2 sans les parenthèses.

Python 2

print 'Python', python_version()print 'Hello, World!'print('Hello, World!')print "text", ; print 'print more text on the same line'
Python 2.7.6Hello, World!Hello, World!text print more text on the same line

Python 3

print('Python', python_version())print('Hello, World!')print("some text,", end="")print(' print more text on the same line')

.

Python 3.4.1Hello, World!some text, print more text on the same line
print 'Hello, World!'
 File "<ipython-input-3-139a7c5835bd>", line 1 print 'Hello, World!' ^SyntaxError: invalid syntax

Note :

Imprimer « Hello, World » ci-dessus via Python 2 semblait tout à fait « normal ». Cependant, si nous avons plusieurs objets à l’intérieur des paranthèses, nous allons créer un tuple, puisque print est une « déclaration » en Python 2, et non un appel de fonction.

print 'Python', python_version()print('a', 'b')print 'a', 'b'
Python 2.7.7('a', 'b')a b

Division d’entiers

Ce changement est particulièrement dangereux si vous portez du code, ou si vous exécutez du code Python 3 dans Python 2, car le changement de comportement de la division d’entiers peut souvent passer inaperçu (il ne lève pas un SyntaxError).
Donc, j’ai toujours tendance à utiliser une float(3)/2 ou une 3/2.0 au lieu d’une 3/2 dans mes scripts Python 3 pour épargner quelques soucis aux gars de Python 2 (et vice versa, je recommande une from __future__ import division dans vos scripts Python 2).

Python 2

print 'Python', python_version()print '3 / 2 =', 3 / 2print '3 // 2 =', 3 // 2print '3 / 2.0 =', 3 / 2.0print '3 // 2.0 =', 3 // 2.0
Python 2.7.63 / 2 = 13 // 2 = 13 / 2.0 = 1.53 // 2.0 = 1.0

Python 3

.

print('Python', python_version())print('3 / 2 =', 3 / 2)print('3 // 2 =', 3 // 2)print('3 / 2.0 =', 3 / 2.0)print('3 // 2.0 =', 3 // 2.0)
Python 3.4.13 / 2 = 1.53 // 2 = 13 / 2.0 = 1.53 // 2.0 = 1.0

Unicode

Python 2 possède des types ASCII str(), des types séparés unicode(), mais pas de type byte.

Maintenant, dans Python 3, nous avons enfin des strings Unicode (utf-8), et 2 classes d’octets : byte et bytearrays.

Python 2

print 'Python', python_version()
Python 2.7.6
.>.

print type(unicode('this is like a python3 str type'))
<type 'unicode'>
print type(b'byte type does not exist')

.

<type 'str'>
print 'they are really' + b' the same'
.

they are really the same
print type(bytearray(b'bytearray oddly does exist though'))
<type 'bytearray'>

.

Python 3

print('Python', python_version())print('strings are now utf-8 \u03BCnico\u0394é!')
Python 3.4.1strings are now utf-8 μnicoΔé!
.

print('Python', python_version(), end="")print(' has', type(b' bytes for storing data'))
Python 3.4.1 has <class 'bytes'>
print('and Python', python_version(), end="")print(' also has', type(bytearray(b'bytearrays')))

.

and Python 3.4.1 also has <class 'bytearray'>
'note that we cannot add a string' + b'bytes for data'
.

---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-13-d3e8942ccf81> in <module>()----> 1 'note that we cannot add a string' + b'bytes for data'TypeError: Can't convert 'bytes' object to str implicitly

xrange

L’utilisation de xrange() est très populaire dans Python 2.x pour créer un objet itérable, par ex, dans un for-loop ou une liste/set-dictionary-comprehension.
Le comportement était assez similaire à celui d’un générateur (c’est-à-dire, « évaluation paresseuse »), mais ici le xrange-iterable n’est pas épuisable – ce qui signifie que vous pourriez itérer sur lui à l’infini.

Grâce à son « évaluation paresseuse », l’avantage du range() régulier est que xrange() est généralement plus rapide si vous devez itérer sur lui une seule fois (par exemple, dans une boucle for). Cependant, contrairement aux itérations à une seule reprise, elle n’est pas recommandée si vous répétez l’itération plusieurs fois, car la génération se produit à chaque fois à partir de zéro !

Dans Python 3, la range() a été implémentée comme la fonction xrange() de sorte qu’une fonction xrange() dédiée n’existe plus (xrange() lève une NameError dans Python 3).

import timeitn = 10000def test_range(n): return for i in range(n): passdef test_xrange(n): for i in xrange(n): pass 

Python 2

print 'Python', python_version()print '\ntiming range()'%timeit test_range(n)print '\n\ntiming xrange()'%timeit test_xrange(n)
.

Python 2.7.6timing range()1000 loops, best of 3: 433 µs per looptiming xrange()1000 loops, best of 3: 350 µs per loop

Python 3

print('Python', python_version())print('\ntiming range()')%timeit test_range(n)
Python 3.4.1timing range()1000 loops, best of 3: 520 µs per loop

.

print(xrange(10))
---------------------------------------------------------------------------NameError Traceback (most recent call last)<ipython-input-5-5d8f9b79ea70> in <module>()----> 1 print(xrange(10))NameError: name 'xrange' is not defined

La méthode __contains____ pour les objets de type range. dans Python 3

Une autre chose qui mérite d’être mentionnée est que range a obtenu une « nouvelle » __contains__ méthode dans Python 3.x (merci à Yuchen Ying, qui l’a signalé). La méthode __contains__ peut accélérer les « consultations » dans Python 3. x range de manière significative pour les types entiers et booléens.

x = 10000000
def val_in_range(x, val): return val in range(x)
def val_in_xrange(x, val): return val in xrange(x)

.

print('Python', python_version())assert(val_in_range(x, x/2) == True)assert(val_in_range(x, x//2) == True)%timeit val_in_range(x, x/2)%timeit val_in_range(x, x//2)
Python 3.4.11 loops, best of 3: 742 ms per loop1000000 loops, best of 3: 1.19 µs per loop

Selon les résultats timeit ci-dessus, vous voyez que l’exécution pour le « look up » était environ 60 000 plus rapide lorsqu’il était de type entier plutôt que flottant. Toutefois, depuis que Python 2.x range ou xrange n’a pas de méthode __contains__, la « vitesse de consultation » ne serait pas tellement différente pour les entiers ou les flottants :

print 'Python', python_version()assert(val_in_xrange(x, x/2.0) == True)assert(val_in_xrange(x, x/2) == True)assert(val_in_range(x, x/2) == True)assert(val_in_range(x, x//2) == True)%timeit val_in_xrange(x, x/2.0)%timeit val_in_xrange(x, x/2)%timeit val_in_range(x, x/2.0)%timeit val_in_range(x, x/2)
Python 2.7.71 loops, best of 3: 285 ms per loop1 loops, best of 3: 179 ms per loop1 loops, best of 3: 658 ms per loop1 loops, best of 3: 556 ms per loop

Après les « preuves » que la méthode __contain__ n’a pas été ajoutée à Python 2.x pour le moment :

print('Python', python_version())range.__contains__
Python 3.4.1<slot wrapper '__contains__' of 'range' objects>
.

print 'Python', python_version()range.__contains__
Python 2.7.7---------------------------------------------------------------------------AttributeError Traceback (most recent call last)<ipython-input-7-05327350dafb> in <module>() 1 print 'Python', python_version()----> 2 range.__contains__AttributeError: 'builtin_function_or_method' object has no attribute '__contains__'
print 'Python', python_version()xrange.__contains__

.

Python 2.7.7---------------------------------------------------------------------------AttributeError Traceback (most recent call last)<ipython-input-8-7d1a71bfee8e> in <module>() 1 print 'Python', python_version()----> 2 xrange.__contains__AttributeError: type object 'xrange' has no attribute '__contains__'

Note sur les différences de vitesse dans Python 2 et 3

Certaines personnes ont souligné la différence de vitesse entre la range() de Python 3 et la xrange() de Python2. Puisqu’ils sont implémentés de la même manière, on pourrait s’attendre à la même vitesse. Cependant la différence ici vient juste du fait que Python 3 a généralement tendance à s’exécuter plus lentement que Python 2.

def test_while(): i = 0 while i < 20000: i += 1 return
print('Python', python_version())%timeit test_while()
Python 3.4.1100 loops, best of 3: 2.68 ms per loop

.

print 'Python', python_version()%timeit test_while()
Python 2.7.61000 loops, best of 3: 1.72 ms per loop

Soulever des exceptions

Lorsque Python 2 accepte les deux notations, l' » ancienne  » et la  » nouvelle  » syntaxe, Python 3 s’étrangle (et lève un SyntaxError à son tour) si nous ne mettons pas l’argument d’exception entre parenthèses :

Python 2

print 'Python', python_version()
Python 2.7.6
raise IOError, "file error"
---------------------------------------------------------------------------IOError Traceback (most recent call last)<ipython-input-8-25f049caebb0> in <module>()----> 1 raise IOError, "file error"IOError: file error
raise IOError("file error")
---------------------------------------------------------------------------IOError Traceback (most recent call last)<ipython-input-9-6f1c43f525b2> in <module>()----> 1 raise IOError("file error")IOError: file error

Python 3

print('Python', python_version())
Python 3.4.1

.

raise IOError, "file error"
 File "<ipython-input-10-25f049caebb0>", line 1 raise IOError, "file error" ^SyntaxError: invalid syntax

La bonne façon de lever une exception en Python 3 :

print('Python', python_version())raise IOError("file error")
Python 3.4.1---------------------------------------------------------------------------OSError Traceback (most recent call last)<ipython-input-11-c350544d15da> in <module>() 1 print('Python', python_version())----> 2 raise IOError("file error")OSError: file error

Gestion des exceptions

Aussi la gestion des exceptions a légèrement changé dans Python 3. Dans Python 3, nous devons utiliser le mot-clé « as » maintenant

Python 2

.

print 'Python', python_version()try: let_us_cause_a_NameErrorexcept NameError, err: print err, '--> our error message'
Python 2.7.6name 'let_us_cause_a_NameError' is not defined --> our error message

Python 3

print('Python', python_version())try: let_us_cause_a_NameErrorexcept NameError as err: print(err, '--> our error message')
Python 3.4.1name 'let_us_cause_a_NameError' is not defined --> our error message

.

Puisque next().next()) est une fonction (méthode) si couramment utilisée, c’est un autre changement de syntaxe (ou plutôt d’implémentation) qui mérite d’être mentionné : là où vous pouvez utiliser à la fois la syntaxe des fonctions et des méthodes dans Python 2.7.5, la fonction next() est tout ce qui reste dans Python 3 (l’appel de la méthode .next() lève un AttributeError).

Python 2

print 'Python', python_version()my_generator = (letter for letter in 'abcdefg')next(my_generator)my_generator.next()
Python 2.7.6'b'

Python 3

print('Python', python_version())my_generator = (letter for letter in 'abcdefg')next(my_generator)

.

Python 3.4.1'a'
my_generator.next()
---------------------------------------------------------------------------AttributeError Traceback (most recent call last)<ipython-input-14-125f388bb61b> in <module>()----> 1 my_generator.next()AttributeError: 'generator' object has no attribute 'next'

For-loop variables and the global namespace leak

Bonne nouvelle : Dans Python 3.x, les variables for-loop ne fuient plus dans l’espace de noms global !

Cela remonte à un changement effectué dans Python 3.x et décrit dans What’s New In Python 3.0 comme suit :

« Les compréhensions de listes ne prennent plus en charge la forme syntaxique . Utilisez plutôt . Notez également que les compréhensions de liste ont une sémantique différente : elles sont plus proches du sucre syntaxique d’une expression de générateur à l’intérieur d’un constructeur list(), et en particulier les variables de contrôle de boucle ne sont plus fuies dans la portée environnante. »

Python 2

print 'Python', python_version()i = 1print 'before: i =', iprint 'comprehension: ', print 'after: i =', i
.

Python 2.7.6before: i = 1comprehension: after: i = 4

Python 3

print('Python', python_version())i = 1print('before: i =', i)print('comprehension:', )print('after: i =', i)
.

Python 3.4.1before: i = 1comprehension: after: i = 1

Comparer des types non ordonnables

Un autre changement agréable dans Python 3 est qu’un TypeError est levé comme avertissement si nous essayons de comparer des types non ordonnables.

Python 2

print 'Python', python_version()print " > 'foo' = ", > 'foo'print "(1, 2) > 'foo' = ", (1, 2) > 'foo'print " > (1, 2) = ", > (1, 2)
Python 2.7.6 > 'foo' = False(1, 2) > 'foo' = True > (1, 2) = False

Python 3

print('Python', python_version())print(" > 'foo' = ", > 'foo')print("(1, 2) > 'foo' = ", (1, 2) > 'foo')print(" > (1, 2) = ", > (1, 2))
Python 3.4.1---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-16-a9031729f4a0> in <module>() 1 print('Python', python_version())----> 2 print(" > 'foo' = ", > 'foo') 3 print("(1, 2) > 'foo' = ", (1, 2) > 'foo') 4 print(" > (1, 2) = ", > (1, 2))TypeError: unorderable types: list() > str()

Paramétrage des entrées utilisateur via input()

Hélas, la fonction input() a été corrigée dans Python 3 afin qu’elle stocke toujours les entrées utilisateur sous forme d’objets str. Afin d’éviter le comportement dangereux de Python 2 de lire dans d’autres types que strings, nous devons utiliser raw_input() à la place.

Python 2

Python 2.7.6 sur darwinTapez "help", "copyright", "credits" ou "license" pour plus d'informations.>>> my_input = input('enter a number : ')entrer un nombre : 123>>> type(my_input)<type 'int'>>>>

my_input = raw_input(‘enter a number : ‘)entrez un nombre : 123>>> type(my_input)<type ‘str’>

Python 3

Python 3.4.1 sur darwinTapez "help", "copyright", "credits" ou "license" pour plus d'informations.>>> my_input = input('enter a number : ')entrer un nombre : 123>>> type(my_input)<class 'str'>

Retourner des objets itérables au lieu de listes

Comme nous l’avons déjà vu dans la section xrange, certaines fonctions et méthodes renvoient désormais des objets itérables dans Python 3 – au lieu de listes dans Python 2.

Puisque nous n’itérons généralement qu’une seule fois sur ces derniers de toute façon, je pense que ce changement a beaucoup de sens pour économiser de la mémoire. Cependant, il est également possible – contrairement aux générateurs – d’itérer sur ceux-ci plusieurs fois si nécessaire, c’est seulement moins efficace.

Et pour les cas où nous avons vraiment besoin des list-objets, nous pouvons simplement convertir l’objet itérable en un list via la fonction list().

Python 2

print 'Python', python_version()print range(3)print type(range(3))
Python 2.7.6<type 'list'>

Python 3

.

print('Python', python_version())print(range(3))print(type(range(3)))print(list(range(3)))
Python 3.4.1range(0, 3)<class 'range'>

Certaines fonctions et méthodes plus couramment utilisées qui ne retournent plus de listes dans Python 3 :

  • zip()

  • map()

  • filter()

  • La méthode .keys() du dictionnaire

  • La méthode du dictionnaire. .values() méthode

  • Dictionnaire .items() méthode

Arrondi de Banker

Python 3 a adopté la manière désormais standard d’arrondir les décimales lorsqu’il en résulte une égalité (.5) aux derniers chiffres significatifs. Désormais, dans Python 3, les décimales sont arrondies au nombre pair le plus proche. Bien que ce soit un inconvénient pour la portabilité du code, c’est censé être une meilleure façon d’arrondir que l’arrondi au chiffre supérieur, car cela évite le biais vers les grands nombres. Pour plus d’informations, voir les excellents articles et paragraphes de Wikipédia :

  • https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
  • https://en.wikipedia.org/wiki/IEEE_floating_point#Roundings_to_nearest

Python 2

print 'Python', python_version()
Python 2.7.12
.

round(15.5)
16.0
round(16.5)
17.0

Python 3

print('Python', python_version())
Python 3.5.1
round(15.5)
16
round(16.5)

.

16

Plus d’articles sur Python 2 et Python 3

Voici une liste de quelques bons articles concernant Python 2 et 3 que je vous recommande comme suivi-.up.

// Portage vers Python 3

  • Dois-je utiliser Python 2 ou Python 3 pour mon activité de développement ?

  • Quelles sont les nouveautés de Python 3.0

  • Portage vers Python 3
  • Portage du code Python 2 vers Python 3
  • Comment faire pour que Python. 3 avance

// Pro et anti Python 3

  • 10 fonctionnalités géniales de Python que vous ne pouvez pas utiliser. parce que vous refusez de passer à Python 3

  • Tout ce que vous ne vouliez pas savoir sur l’Unicode dans Python 3
  • .

  • Python 3 tue Python

  • Python 3 peut faire revivre Python
  • Python 3 est très bien

.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *