35 votes

Comment écrire Python 2.x en étant le plus compatible possible avec Python 3.x ?

Il existe de nombreuses façons de inclure les fonctionnalités de Python 3.x dans Python 2.x afin que le code des scripts Python 2.x puisse être facilement converti en Python 3.x à l'avenir. L'un de ces exemples consiste à remplacer print déclaration avec print() fonction :

>>> from __future__ import print_function

Existe-t-il une liste ou une ressource qui pourrait donner des idées sur la manière de rendre le code Python 2.x aussi proche que possible de Python 3.x ?

Pouvez-vous donner des exemples d'autres importations ou définitions qui peuvent faire en sorte que Python 2.x ressemble et se comporte davantage comme Python 3.x ?

Supposons que nous ayons à notre disposition la dernière version de Python 2.x (2.7.2 pour le moment, je crois).

22voto

dstromberg Points 3126

Je mets la touche finale à un programme de sauvegarde dédupliqué d'environ 5000 lignes ( http://stromberg.dnsalias.org/~strombrg/backshift/ ) qui fonctionne sur CPython 2.[567], CPython 3.[0123] (3.3 est encore alpha 0), Pypy 1.7 et Jython trunk. J'ai également essayé IronPython, mais c'était une chose assez différente - il n'avait pas de bibliothèque standard, donc pas de backshift love. Oh, et il peut utiliser Cython pour sa boucle la plus interne, ou psyco - mais pypy est plus rapide que les deux, surtout sur les systèmes 32 bits.

Quoi qu'il en soit, j'ai découvert que pour écrire du code qui fonctionne aussi bien sous 2.x que sous 3.x, il suffisait de faire ce qui suit :

1) print(variable) fonctionne de la même manière sur 2.x et 3.x. print(variable1, variable2) ne fonctionne pas. En 2.x, print(variable) dit "évalue cette expression entre parenthèses, et imprime le résultat unique en utilisant l'instruction print". En 3.x, print(variable) dit "appeler la fonction print sur ce résultat unique". Donc print('abc %d %d' % (1, 2)) fonctionne bien dans les deux cas, car il s'agit d'un résultat à valeur unique, et les deux utilisent l'opérateur % pour le formatage des chaînes.

2) Évitez les constantes octales. Au lieu d'écrire 0755, écrivez (7*64 + 5*8 + 5).

3) Pour faire des E/S binaires dans l'un ou l'autre, j'ai utilisé mon module bufsock. http://stromberg.dnsalias.org/~strombrg/bufsock.html J'ouvrirais un fichier avec os.open, et l'envelopperait avec bufsock (ou utiliserait la classe rawio dans le module). Sous 2.x, cela renverrait une chaîne d'octets codée en chaînes de caractères 8 bits. Sous 3.x, cela renvoie un objet bytes, qui agit comme une liste de petits entiers. Ensuite, j'ai simplement fait passer l'un ou l'autre, en testant avec "isinstance(foo, str)" si nécessaire pour distinguer les deux. Je l'ai fait parce que pour un programme de sauvegarde, les octets sont des octets - je ne voulais pas m'embrouiller avec des encodages qui pourraient nuire à la fiabilité de la sauvegarde des données, et tous les encodages ne font pas un bon aller-retour.

4) Lorsque vous faites des exceptions, évitez le mot clé "as". Utilisez plutôt EG :

  try:
     self.update_timestamp()
  except (OSError, IOError):
     dummy, utime_extra, dummy = sys.exc_info()
     if utime_extra.errno == errno.ENOENT:

5) Un certain nombre de modules ont été renommés lors de la transition de 2.x à 3.x. Essayez donc d'importer l'un ou l'autre dans un module autrement vide, avec quelque chose comme :

try:
   from anydbm import *
except ImportError:
   from dbm import *

...cela apparaîtrait dans un module à part entière, avec un nom EG adbm.py. Ensuite, chaque fois que j'ai besoin d'un magasin de valeurs clés, j'importe adbm au lieu des deux choses différentes nécessaires pour 2.x ou 3.x directement. Ensuite, j'ai pylint tout sauf ce module stubby, adbm.py - et les choses comme lui que pylint n'aimait pas. L'idée était de pylindre tout ce qui était possible, avec des exceptions à la règle "tout doit être pylint" dans un petit module à part, une exception par module.

6) Il est très utile de mettre en place des tests unitaires automatiques et des tests système qui fonctionnent sur 2.x et 3.x, puis de tester fréquemment sur au moins un interpréteur 2.x ainsi que sur au moins un interpréteur 3.x. J'exécute aussi souvent pylint sur mon code, mais seulement un pylint qui vérifie la conformité à la version 2.5.x - j'ai commencé le projet avant que pylint n'obtienne le support de la version 3.x.

7) J'ai mis en place un petit module "python2x3" qui a quelques constantes et appelables pour faciliter la vie : http://stromberg.dnsalias.org/svn/python2x3/trunk/python2x3.py

8) Les littéraux "b" ne fonctionnent pas en 2.5, bien qu'ils fonctionnent plus ou moins en 2.[67]. Au lieu d'essayer de prétraiter ou quelque chose, j'ai mis en place un constants_mod.py qui avait beaucoup de choses qui seraient normalement des littéraux b'' en 3.x, et les a convertis d'une simple chaîne de caractères à ce que le type "bytes" est pour 2.x ou 3.x. Ainsi, ils sont convertis une fois à l'importation du module, et non pas à plusieurs reprises au moment de l'exécution. Si vous visez 2.[67] et plus, il y a peut-être une meilleure façon de faire, mais quand j'ai commencé, le projet Pypy n'était compatible qu'avec 2.5, et Jython l'est toujours.

9) En 2.x, les entiers longs ont un suffixe L. En 3.x, tous les entiers sont longs. J'ai donc décidé d'éviter autant que possible les constantes d'entiers longs ; 2.x fera passer un entier en entier long si nécessaire, donc cela semble bien fonctionner pour la plupart des choses.

10) Il est très utile de disposer d'un certain nombre d'interpréteurs python pour faire des tests. J'ai construit 2.[567] et 3.[0123] et les ai stockés dans /usr/local/cpython-x.y/ pour faciliter les tests. J'ai également mis quelques Pypy et Jython dans /usr/local, toujours pour faciliter les tests. Avoir un script pour automatiser les constructions de CPython était plutôt précieux.

Je crois que ce sont toutes les contorsions que j'ai dû faire pour obtenir une base de code python hautement portable dans un projet non trivial. La seule grande omission dans la liste que j'ai écrite ci-dessus, c'est que je n'essaie pas d'utiliser des objets unicode - c'est quelque chose que quelqu'un d'autre est probablement mieux qualifié pour commenter.

HTH

7voto

DNS Points 17577

Vous devriez vérifier Portage du code Python vers 3.0 . Bien qu'il soit destiné au portage, il répond essentiellement à la même question ; vous n'irez simplement pas jusqu'au bout.

7voto

Lennart Regebro Points 52510

Il y a un chapitre entier à ce sujet dans " Portage vers Python 3 ". Ne manquez pas non plus les annexes, qui répertorient les différences de langue avec des solutions de contournement pour prendre en charge les deux langues.

Vous voulez probablement utiliser le six bibliothèques bien qu'il soit possible de le faire sans.

4voto

Guandalino Points 3287

Portage du code Python 2 vers Python 3 fait partie de la documentation officielle et, bien que ne répondant pas directement à votre question, pourrait vous aider.

1voto

Michael Hoffman Points 8557

J'ai ceci en haut de mon modèle Python 2.7 script :

from __future__ import division, print_function
from future_builtins import ascii, filter, hex, map, oct, zip

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X