¿Cómo escribir Python 2.x lo más compatible posible con Python 3.x?

Hay muchas formas de incluir características de Python 3.x en Python 2.x , por lo que el código de los scripts de Python 2.x se podría convertir fácilmente en Python 3.x en el futuro. Uno de estos ejemplos es reemplazar print statement de print() con print() función de print() :

 >>> from __future__ import print_function 

¿Hay alguna lista o recurso que pueda dar una idea de cómo hacer que el código de Python 2.x esté lo más cerca posible de Python 3.x?

¿Podría dar ejemplos de otras importaciones o definiciones útiles que puedan hacer que Python 2.x se vea y se comporte más como Python 3.x ?

Supongamos que tenemos la última versión de Python 2.x (2.7.2 en este momento, creo) a nuestra disposición.

Estoy dando los toques finales a un progtwig de copia de seguridad de deduplicación de aproximadamente 5000 líneas ( http://stromberg.dnsalias.org/~strombrg/backshift/ ) que se ejecuta en CPython 2. [567], CPython 3. [0123] (3.3 sigue siendo alfa 0), Pypy 1.7 y Jython trunk. También probé IronPython, pero era algo muy diferente: no tenía una biblioteca estándar, por lo que no había un cambio de turno. Ah, y puede usar Cython para su bucle más interno, o psyco, pero pypy es más rápido que cualquiera, especialmente en sistemas de 32 bits.

De todos modos, encontré que para escribir código que se ejecute igual de bien en 2.xy 3.x todo lo que tenía que hacer era:

1) imprimir (variable) funciona igual tanto en 2.xy 3.x. imprimir (variable1, variable2) no lo hace. Para 2.x, print (variable) dice “evalúa esta expresión entre paréntesis e imprime el único resultado utilizando la statement de impresión”. Para 3.x, print (variable) dice “llamar a la función de impresión en este único resultado. Entonces print (‘abc% d% d’% (1, 2)) funciona bien en ambos, porque es un resultado de un solo valor, y ambos asimilan el operador% para el formato de cadena.

2) Evitar las constantes octales. En lugar de escribir 0755, escriba (7 * 64 + 5 * 8 + 5).

3) Para hacer E / S binarias en cualquiera de los dos, utilicé mi módulo bufsock. http://stromberg.dnsalias.org/~strombrg/bufsock.html Yo abriría un archivo y lo envolvería con bufsock (o usaría la clase rawio en el módulo). En 2.x, esto devolvería una cadena de bytes codificados como cadenas de caracteres de 8 bits. En 3.x, esto devolvería un objeto de bytes, que se parece mucho a una lista de enteros pequeños. Luego simplemente pasé una o la otra, probando con “isinstance (foo, str)” según sea necesario para distinguir entre las dos. Hice esto, porque para un progtwig de copia de seguridad, los bytes son bytes. No quería meterme con las codificaciones, incrustando el guardado de datos de forma confiable, y no todas las codificaciones se realizan correctamente.

4) Al hacer excepciones, evite la palabra clave “como”. En su lugar, utilice EG:

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

5) Se cambió el nombre de un grupo de módulos en la transición de 2.x a 3.x. Así que intente importar cualquiera de ellos en un módulo que de otra manera esté vacío, con algo como:

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

… esto aparecería en un módulo por sí mismo, con un nombre EG adbm.py. Entonces, en cualquier momento en que necesitara un almacén de valor-clave, importaría adbm en lugar de las dos cosas diferentes necesarias para 2.xy 3.x directamente. Luego pylint todo, excepto ese módulo rechoncho, adbm.py – y cosas como esa pylint no me gustó. La idea era crear un pylint de todo lo posible, con excepciones a la regla de “todo se debe a un pylint” en un pequeño módulo por sí mismo, una excepción por módulo.

6) Es muy útil configurar las pruebas unitarias automáticas y las pruebas del sistema que se ejecutan en 2.xy 3.x, y luego realizar pruebas con frecuencia en al menos un intérprete 2.x así como al menos un intérprete 3.x. También ejecuto pylint contra mi código a menudo, aunque solo es un pylint que verificó el cumplimiento de 2.5.x – Comencé el proyecto antes de que pylint obtuviera soporte 3.x.

7) Configuré un pequeño módulo “python2x3” que tiene algunas constantes y callables para hacer la vida más fácil: http://stromberg.dnsalias.org/svn/python2x3/trunk/python2x3.py

8) Los literales b ” no funcionan en 2.5, aunque funcionan en 2. [67]. En lugar de tratar de preprocesar o algo, configuré constantes_mod.py que tenían muchas cosas que normalmente serían b ” literales en 3.x, y las convertí de una cadena simple a cualquiera que sea el tipo de “bytes” para 2 .x o 3.x. Así que se convierten una vez en la importación de módulos, no una y otra vez en tiempo de ejecución. Si estás apuntando a 2. [67] y arriba, quizás haya una mejor manera, pero cuando comencé el proyecto Pypy solo era compatible con 2.5, y Jython aún lo es.

9) En 2.x, los enteros largos tienen un sufijo L. En 3.x, todos los enteros son largos. Así que me limité a evitar constantes enteras largas tanto como sea posible; 2.x promoverá un número entero a largo como sea necesario, por lo que esto parece funcionar bien para la mayoría de las cosas.

10) Es de gran ayuda tener a muchos intérpretes de Python para probar. Construí 2. [567] y 3. [0123] y los guardó en / usr / local / cpython-xy / para facilitar las pruebas. También puse algunos Pypy’s y Jython’s en / usr / local, nuevamente para una fácil prueba. Tener un script para automatizar las comstackciones de CPython era bastante valioso.

Creo que estas fueron todas las contorsiones que necesitaba para obtener una base de código Python altamente portátil en un proyecto no trivial. La única gran omisión en la lista que escribí anteriormente es que no estoy tratando de usar objetos Unicode, eso es algo que probablemente alguien más esté mejor calificado para comentar.

HTH

Debes revisar el código de portes de Python a 3.0 . Si bien está dirigido a la adaptación, responde esencialmente a la misma pregunta; Simplemente no vas a ir todo el camino.

Hay un capítulo completo sobre esto en ” Portar a Python 3 “. Tampoco se pierda los apéndices, que enumeran las diferencias de idioma con soluciones alternativas para admitir ambos idiomas.

Probablemente quieras usar la biblioteca seis , aunque es posible hacerlo sin ella.

Portar el código de Python 2 a Python 3 es parte de la documentación oficial y, aunque no responda directamente a su pregunta, podría ayudar.

Tengo esto en la parte superior de mi plantilla de script de Python 2.7:

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

Como aún no se ha mencionado: esta hoja de trucos me parece muy útil para este propósito exacto.