Preparándose para convertir de Python 2.x a 3.x

Como todos ya sabemos (espero), Python 3 está comenzando a reemplazar Python 2.x lentamente. Por supuesto, pasarán muchos MUCHOS años antes de que la mayoría del código existente sea finalmente portado, pero hay cosas que podemos hacer ahora mismo en nuestro código de la versión 2.x para facilitar el cambio.

Obviamente, echar un vistazo a las novedades de 3.x será útil, pero ¿qué podemos hacer ahora mismo para que la próxima conversión sea más fácil (además de facilitar el envío de actualizaciones a versiones concurrentes si es necesario)? Estoy pensando específicamente en las líneas con las que podemos iniciar nuestros scripts que harán que las versiones anteriores de Python sean más similares a 3.x, aunque otros hábitos también son bienvenidos.

El código más obvio para agregar a la parte superior del script que puedo imaginar es:

from __future__ import division from __future__ import print_function try: range = xrange except NameError: pass 

El hábito más obvio que se me ocurre es el "{0} {1}!".format("Hello", "World") para el formato de cadena.

¿Alguna otra línea y buenos hábitos para entrar?

El mayor problema que no se puede abordar adecuadamente con los cambios de nivel micro y 2to3 es el cambio del tipo de cadena predeterminado de bytes a Unicode.

Si su código necesita hacer algo con las codificaciones y la E / S de bytes, necesitará un montón de esfuerzo manual para convertir correctamente, de modo que las cosas que deben ser bytes sigan siendo bytes, y se decodifiquen adecuadamente en la etapa correcta. Descubrirá que algunos métodos de cadena (en particular el format() ) y las llamadas a la biblioteca requieren cadenas Unicode, por lo que es posible que necesite ciclos adicionales de deencoding / encoding para usar las cadenas como Unicode, incluso si en realidad son solo bytes.

Esto no se ve afectado por el hecho de que algunos de los módulos de la biblioteca estándar de Python se han convertido de forma cruda utilizando 2to3 sin la debida atención a los problemas de bytes / Unicode / encoding, y por lo tanto cometen errores sobre qué tipo de cadena es apropiado. Algo de esto se está analizando, pero al menos desde Python 3.0 a 3.2 se enfrentará a un comportamiento confuso y potencialmente defectuoso de paquetes como urllib, correo electrónico y wsgiref que necesitan conocer las codificaciones de bytes.

Puede mejorar el problema teniendo cuidado cada vez que escriba una cadena literal. Use u'' cadenas u'' para cualquier cosa que esté inherentemente basada en caracteres, b'' cadenas para cualquier cosa que sea realmente bytes, y '' para el tipo de ‘cadena predeterminada’ donde no importa o si necesita cumplir los requisitos de uso de cadenas de una llamada de biblioteca .

Desafortunadamente, la syntax b'' solo se introdujo en Python 2.6, por lo que al hacerlo se eliminan los usuarios de versiones anteriores.

eta:

¿cual es la diferencia?

Oh mi. Bien…

Un byte contiene un valor en el rango de 0 a 255, y puede representar una carga de datos binarios (por ejemplo, el contenido de una imagen) o algún texto, en cuyo caso debe haber un estándar elegido para la forma de asignar un conjunto de caracteres en esos bytes. La mayoría de estos estándares de ‘encoding’ asignan el conjunto de caracteres ‘ASCII’ normal a los bytes 0–127 de la misma manera, por lo que generalmente es seguro usar cadenas de bytes para el procesamiento de texto solo ASCII en Python 2.

Si desea utilizar cualquiera de los caracteres fuera del conjunto ASCII en una cadena de bytes, tiene problemas, porque cada encoding asigna un conjunto diferente de caracteres a los valores de bytes restantes 128–255, y la mayoría de las codificaciones no pueden asignar cada Posible caracter a bytes. Esta es la fuente de todos esos problemas en los que carga un archivo de una configuración regional en una aplicación de Windows en otra configuración regional y todas las letras acentuadas o no latinas cambian a las incorrectas, lo que genera un desorden ilegible. (también conocido como ‘mojibake’.)

También hay codificaciones ‘multibyte’, que intentan ajustar más caracteres en el espacio disponible utilizando más de un byte para almacenar cada carácter. Estos se introdujeron para los entornos locales de Asia oriental, ya que hay muchos caracteres chinos. Pero también hay UTF-8, una encoding multibyte moderna mejor diseñada que puede acomodar a cada personaje.

Si está trabajando en cadenas de bytes en una encoding multibyte, y probablemente lo haga hoy, porque UTF-8 se usa mucho; en realidad, no se debe usar ninguna otra encoding en una aplicación moderna, entonces tiene aún más problemas que solo hacer un seguimiento de la encoding con la que está jugando. len() te dirá la longitud en bytes, no la longitud en caracteres, y si comienzas a indexar y alterar los bytes, es muy probable que rompas una secuencia multibyte en dos, generando una secuencia no válida y generalmente confundiendo todo .

Por esta razón, Python 1.6 y posteriores tienen cadenas nativas de Unicode (escritas con u'something' ), donde cada unidad en la cadena es un carácter, no un byte. Puedes len() agruparlos, cortarlos, reemplazarlos, volverlos a express, y siempre se comportarán de manera apropiada. Las tareas de procesamiento de texto son indudablemente mejores, por lo que Python 3 las convierte en el tipo de cadena predeterminado (sin tener que poner una u antes del '' ).

El problema es que muchas de las interfaces existentes, como los nombres de archivo en sistemas operativos distintos de Windows, o HTTP o SMTP, se basan principalmente en bytes, con una forma separada de especificar la encoding. Por lo tanto, cuando se trata de componentes que necesitan bytes, debe codificar correctamente las cadenas Unicode a bytes, y en Python 3 tendrá que hacerlo explícitamente en algunos lugares donde antes no era necesario.

Es un detalle de implementación interna que las cadenas Unicode toman ‘dos ​​bytes’ de almacenamiento por unidad internamente. Nunca puedes ver ese almacenamiento; No deberías pensar en términos de bytes. Las unidades en las que está trabajando son conceptualmente caracteres, independientemente de cómo elija Python representarlos en la memoria.

…aparte:

Esto no es del todo cierto. En las ‘comstackciones estrechas’ de Python como la comstackción de Windows, cada unidad de una cadena Unicode no es técnicamente un carácter, sino una ‘unidad de código’ UTF-16. Para los personajes en el Plano Multilingüe Básico, de 0x0000–0xFFFF, no notará ninguna diferencia, pero si está usando caracteres fuera de este rango de 16 bits, aquellos en los ‘planos astrales’, encontrará que toman dos unidades en lugar de una, y, de nuevo, te arriesgas a dividir un personaje cuando los recortas.

Esto es bastante malo, y ha ocurrido porque Windows (y otros, como Java) se establecieron en UTF-16 como un mecanismo de almacenamiento en memoria antes de que Unicode creciera más allá del límite de 65,000 caracteres. Sin embargo, el uso de estos caracteres extendidos todavía es bastante raro, y cualquier persona en Windows estará acostumbrada a interrumpir en muchas aplicaciones, por lo que es probable que no sea crítico para usted.

En “comstackciones anchas”, las cadenas Unicode están formadas por unidades de “puntos de código” de caracteres reales, por lo que incluso los caracteres extendidos fuera del BMP pueden manejarse de manera consistente y fácil. El precio que se paga por esto es la eficiencia: cada unidad de cadena ocupa cuatro bytes de almacenamiento en la memoria.

Estoy tratando de acostumbrarme a usar cosas como var1//var2 siempre que quiera una división entera (y no un flotador). No es un gran paso hacia Python 3, pero al menos no tendré que regresar y revisar toda mi división 🙂