Python 3: codificaciones y más

sábado, 21 de febrero de 2009
Unicode, bytes y strings

Otro punto importante de cambios en Python 3 es como el lenguaje trata el texto, unificando todos los strings bajo Unicode. Históricamente, Python tenía un tipo especial de string Unicode, con una sintaxis especial, que era hermano del str convencional (dado que ambos heredaban de BaseString).

Esta distinción entre str y unicode como tipos básicos se efectuaba en el código fuente con su sintaxis específica:
str_normal = "Hola Mundo!"
str_unicode = u"Hola Mundo!" # nótese la 'u' antes de la primera comilla
Sin embargo, este paradigma no era muy feliz. Un str convencional carecía de una codificación particular y podía producir una serie de ambigüedades, en particular a la hora de realizar comparaciones entre cadenas de texto Unicode y cadenas de texto de 8 bits convencionales (en muchos casos se pueden producir Warnings o incluso excepciones).

A partir de Python 3, se utiliza una convención mucho más apropiada. Existen finalmente dos tipos básicos para las finalidades en las que regularmente se utilizan strings:
  • bytes, que es una secuencia de caracteres de 8 bits sin ninguna codificación particular
  • texto, que es una secuencia de caracteres textuales, y como tal representa palabras u otro texto y soporta la noción de encoding
La sintaxis para éstos es:
str_bytes = b"Hola Mundo!" # nótese la 'b' antes de la primera comilla
str_texto = "Hola Mundo!"
Esto tiene como consecuencia que al momento de declarar un string (y como lo más usual es utilizarlas para almacenar texto), se utilizará una cadena Unicode como corresponde para tener correcto soporte de internacionalización. Para el manejo de datos binarios o blobs de caracteres sin interpretación, el tipo bytes está disponible.

Codificación del código fuente

A su vez, y dado que Python históricamente no soportaba varias codificaciones de fuente, a partir de Python 2.0 (y junto con la introducción del soporte Unicode) se introdujo la posibilidad de declarar la codificación de un archivo mediante un comentario especial. Finalmente, desde 2.5, la aparición de caracteres no-ASCII en un archivo de fuente Python que no tuviera su codificación declarada producía un error.

A partir de Python 3, la codificación por defecto es UTF-8 a menos que se indique lo contrario utilizando la declaración que existe desde Python 2. Este valor por defecto es más razonable (ya que UTF-8 es el estándar de facto en la mayoría de los entornos modernos) y evita la necesidad de estar especificando permanentemente la codificación cada vez que debe crearse un nuevo archivo de código fuente.

En resumen

Una vez más se reemplazaron algunos casos por defecto para hacerlos más apropiados, rompiendo así la compatibilidad hacia atrás en pos de la sanidad del código fuente. El soporte de cadenas de texto internacionalizadas es ahora transparente y no requiere ningún esfuerzo adicional por parte del programador. Asimismo, la modificación en el source encoding ahorra un poco de texto innecesario al comienzo de cada nuevo archivo, manteniendo la esencia minimalista del código fuente de Python.

Como siempre, hay un resumen completo de los cambios en el changelog de Python 3. Además, hay una discusión interesante en Slashdot respecto de este tema, donde además se discuten las diferencias respecto del método adoptado por Ruby para soporte Unicode (es interesante puesto que Ruby tiene sus raíces en países de Asia, que es donde Unicode recibe más críticas por supuestas limitaciones técnicas).

2 comentarios:

Pablo Antonio dijo...

Gracias por seguir con esta serie.

Me queda una duda: Decís que el tipo bytes no tiene ninguna codificación. Eso tiene sentido. Ahora, en el ejemplo en el que le asignás algo a str_bytes, ¿cómo se interpreta el texto asignado? Quiero decir, no es lo mismo si es UTF8 o es latin1 o es ASCII; lo que queda en memoria (la tira de bytes) es distinto.

Espero que la pregunta no te parezca demasiado pava :)

Saludos.

GomoX dijo...

Hola Pablo,

Se hizo larga la serie, un post más y la liquido creo :) La pregunta es buena, yo me pregunté lo mismo.

El texto asignado se interpreta según la codificación del archivo de código fuente. Esto es, si hay bytes no-ASCII en Python 2.x se interpreta según lo que diga el campo coding (el "comentario especial" del que hablo arriba) en la parte superior del archivo, y en Python 3 se interpreta como UTF-8 (salvo que el campo coding indique otra cosa).

Saludos ;)

Publicar un comentario