¿Por qué declarar unicode por cadena en python?

122

Todavía estoy aprendiendo Python y tengo una duda:

En python 2.6.x, generalmente declaro la codificación en el encabezado del archivo de esta manera (como en PEP 0263 )

# -*- coding: utf-8 -*-

Después de eso, mis cadenas se escriben como de costumbre:

a = "A normal string without declared Unicode"

Pero cada vez que veo un código de proyecto de Python, la codificación no se declara en el encabezado. En cambio, se declara en cada cadena de esta manera:

a = u"A string with declared Unicode"

¿Cual es la diferencia? ¿Cuál es el propósito de esto? Sé que Python 2.6.x establece la codificación ASCII de forma predeterminada, pero puede anularse mediante la declaración de encabezado, entonces, ¿cuál es el punto de la declaración por cadena?

Anexo: Parece que he mezclado la codificación de archivos con la codificación de cadenas. Gracias por explicarlo :)

Oscar Carballal
fuente
66
# coding: utf8es lo suficientemente bueno, no es necesario-*-
medusas
1
@ medusa Supongo que querías escribir # coding: utf-8.
Samuel Harmer
Debe ser #coding=utf-8. python.org/dev/peps/pep-0263
Shen

Respuestas:

167

Esas son dos cosas diferentes, como han mencionado otros.

Cuando especificas# -*- coding: utf-8 -*- , le estás diciendo a Python que el archivo fuente que has guardado es utf-8. El valor predeterminado para Python 2 es ASCII (para Python 3 es utf-8). Esto solo afecta la forma en que el intérprete lee los caracteres del archivo.

En general, probablemente no sea la mejor idea incrustar caracteres unicode altos en su archivo sin importar la codificación; puede usar cadenas de escape unicode, que funcionan en cualquiera de las codificaciones.


Cuando declaras una cadena con un ufrente , como u'This is a string', le dice al compilador de Python que la cadena es Unicode, no bytes. El intérprete maneja esto de forma transparente; La diferencia más obvia es que ahora puede incrustar caracteres Unicode en la cadena (es decir, u'\u2665'ahora es legal). Puede usarlo from __future__ import unicode_literalspara que sea el predeterminado.

Esto solo se aplica a Python 2; en Python 3, el valor predeterminado es Unicode, y debe especificar un bfrente (como b'These are bytes', para declarar una secuencia de bytes).

Chris B.
fuente
¡Gracias por la explicación! Estableceré esto como aceptado ya que es el más completo :)
Oscar Carballal
2
La codificación de origen predeterminada para Python 2 es ascii .
Mark Tolonen
27
En realidad, es una gran idea incrustar caracteres altos Unicode en su archivo. Dudo que los que no hablan inglés quieran leer escapes unicode en sus cadenas.
Mark Tolonen
@ Mark: Gracias por la corrección ASCII; Rápidamente hojeé el PEP ( python.org/dev/peps/pep-0263 ) y habla sobre Latin-1 en el preámbulo. No creo que sea una buena idea incrustar caracteres unicode altos en su archivo en la mayoría de los casos. Ciertamente, si está codificando muchas cadenas que no están en inglés en su archivo fuente, puede facilitarlo, pero generalmente lo hace para mostrarlo al usuario, y probablemente debería definirlas en un lugar separado de todos modos. Y un solo editor de texto mal configurado puede corromper todos esos caracteres.
Chris B.
44
acordó si está programando una aplicación i18nalized, pero considere si es un programador chino o francés. No son solo las cadenas, sino también los comentarios. Es genial que Python sea flexible con codificaciones de origen. Python 3 puede incluso tener caracteres no ASCII en nombres de variables.
Mark Tolonen
23

Como han dicho otros, # coding:especifica la codificación en la que se guarda el archivo fuente. Aquí hay algunos ejemplos para ilustrar esto:

Un archivo guardado en el disco como cp437 (codificación de mi consola), pero sin codificación declarada

b = 'über'
u = u'über'
print b,repr(b)
print u,repr(u)

Salida:

  File "C:\ex.py", line 1
SyntaxError: Non-ASCII character '\x81' in file C:\ex.py on line 1, but no
encoding declared; see http://www.python.org/peps/pep-0263.html for details

Salida de archivo con # coding: cp437agregado:

über '\x81ber'
über u'\xfcber'

Al principio, Python no sabía la codificación y se quejó del carácter no ASCII. Una vez que conocía la codificación, la cadena de bytes obtuvo los bytes que estaban realmente en el disco. Para la cadena Unicode, Python leyó \ x81, sabía que en cp437 eso era un ü , y lo decodificó en el punto de código Unicode para ü que es U + 00FC. Cuando se imprimió la cadena de bytes, Python envió el valor hexadecimal 81a la consola directamente. Cuando se imprimió la cadena Unicode, Python detectó correctamente la codificación de mi consola como cp437 y tradujo Unicode ü al valor cp437 para ü .

Esto es lo que sucede con un archivo declarado y guardado en UTF-8:

├╝ber '\xc3\xbcber'
über u'\xfcber'

En UTF-8, ü se codifica como bytes hexadecimales C3 BC, por lo que la cadena de bytes contiene esos bytes, pero la cadena Unicode es idéntica al primer ejemplo. Python leyó los dos bytes y lo decodificó correctamente. Python imprimió la cadena de bytes incorrectamente, porque envió los dos bytes UTF-8 que representan ü directamente a mi consola cp437.

Aquí el archivo se declara cp437, pero se guarda en UTF-8:

├╝ber '\xc3\xbcber'
├╝ber u'\u251c\u255dber'

La cadena de bytes todavía tenía los bytes en el disco (bytes hexadecimales UTF-8 C3 BC), pero los interpretó como dos caracteres cp437 en lugar de un solo carácter codificado en UTF-8. Esos dos caracteres se tradujeron a puntos de código Unicode, y todo se imprime incorrectamente.

Mark Tolonen
fuente
10

Eso no establece el formato de la cadena; establece el formato del archivo. Incluso con ese encabezado, "hello"es una cadena de bytes, no una cadena Unicode. Para hacerlo Unicode, tendrás que usarlo en u"hello"todas partes. El encabezado es solo una pista de qué formato usar al leer el .pyarchivo.

icktoofay
fuente
Me equivoqué entonces, pensé que eran lo mismo. Entonces, ¿el uso de cadenas unicode es i18n?
Oscar Carballal
@Oscar: Sí, en su mayor parte. Si estaba haciendo un sitio web con Django o algo así y tenía que manejar personas con caracteres no ASCII, entonces ese es otro uso posible.
icktoofay
7

La definición del encabezado es definir la codificación del código en sí, no las cadenas resultantes en tiempo de ejecución.

poner un carácter no ascii como ۲ en el script de python sin la definición del encabezado utf-8 arrojará una advertencia

error

ebt
fuente
-1

Hice el siguiente módulo llamado unicoder para poder hacer la transformación en variables:

import sys
import os

def ustr(string):

    string = 'u"%s"'%string

    with open('_unicoder.py', 'w') as script:

        script.write('# -*- coding: utf-8 -*-\n')
        script.write('_ustr = %s'%string)

    import _unicoder
    value = _unicoder._ustr

    del _unicoder
    del sys.modules['_unicoder']

    os.system('del _unicoder.py')
    os.system('del _unicoder.pyc')

    return value

Luego, en su programa, podría hacer lo siguiente:

# -*- coding: utf-8 -*-

from unicoder import ustr

txt = 'Hello, Unicode World'
txt = ustr(txt)

print type(txt) # <type 'unicode'>
tecnobillo
fuente