¿La mejor manera de convertir cadenas a bytes en Python 3?

861

Parece que hay dos formas diferentes de convertir una cadena a bytes, como se ve en las respuestas a TypeError: 'str' no es compatible con la interfaz del búfer

¿Cuál de estos métodos sería mejor o más pitónico? ¿O es solo una cuestión de preferencia personal?

b = bytes(mystring, 'utf-8')

b = mystring.encode('utf-8')
Mark Ransom
fuente
42
Usar codificación / decodificación es más común y quizás más claro.
Lennart Regebro
11
@LennartRegebro lo despido. Incluso si es más común, al leer "bytes ()" sé lo que está haciendo, mientras que codificar () no me hace sentir que está codificando en bytes.
m3nda
2
@ erm3nda que es una buena razón para usarlo hasta que no se siente así, entonces usted es un paso más cerca de Unicode zen.
Lennart Regebro
44
@LennartRegebro Me siento lo suficientemente bien como para usarlo bytes(item, "utf8"), ya que lo explícito es mejor que lo implícito, así que ... por str.encode( )defecto es silencioso en bytes, lo que te hace más Unicode-zen pero menos Explicit-Zen. También "común" no es un término que me gusta seguir. Además, bytes(item, "utf8")es más como el str(), y las b"string"anotaciones. Mis disculpas si soy tan novato para entender sus razones. Gracias.
m3nda
44
@ erm3nda si lees la respuesta aceptada puedes ver que encode()no llama bytes(), es al revés. Por supuesto, eso no es inmediatamente obvio, por eso hice la pregunta.
Mark Ransom

Respuestas:

571

Si miras los documentos bytes, te indica bytearray:

bytearray ([fuente [, codificación [, errores]]])

Devuelve una nueva matriz de bytes. El tipo bytearray es una secuencia mutable de enteros en el rango 0 <= x <256. Tiene la mayoría de los métodos habituales de secuencias mutables, descritos en Tipos de secuencia mutable, así como la mayoría de los métodos que tiene el tipo de bytes, vea Bytes y Métodos de matriz de bytes.

El parámetro fuente opcional se puede usar para inicializar la matriz de diferentes maneras:

Si es una cadena, también debe proporcionar los parámetros de codificación (y opcionalmente, errores); bytearray () luego convierte la cadena a bytes usando str.encode ().

Si es un entero, la matriz tendrá ese tamaño y se inicializará con bytes nulos.

Si es un objeto conforme con la interfaz del búfer, se usará un búfer de solo lectura del objeto para inicializar la matriz de bytes.

Si es un iterable, debe ser un iterable de enteros en el rango 0 <= x <256, que se utilizan como el contenido inicial de la matriz.

Sin un argumento, se crea una matriz de tamaño 0.

Por byteslo tanto, puede hacer mucho más que solo codificar una cadena. Es Pythonic que le permitiría llamar al constructor con cualquier tipo de parámetro fuente que tenga sentido.

Para codificar una cadena, creo que some_string.encode(encoding)es más Pythonic que usar el constructor, porque es el más autodocumentado: "tomar esta cadena y codificarla con esta codificación" es más claro que bytes(some_string, encoding): no hay un verbo explícito cuando se usa el constructor.

Editar: Revisé la fuente de Python. Si pasa una cadena unicode a bytesCPython, llama a PyUnicode_AsEncodedString , que es la implementación de encode; así que solo te estás saltando un nivel de indirección si te llamas a encodeti mismo.

Además, vea el comentario de Serdalis: unicode_string.encode(encoding)también es más pitónico porque es inverso byte_string.decode(encoding)y la simetría es agradable.

agf
fuente
73
+1 por tener un buen argumento y citas de los documentos de Python. También unicode_string.encode(encoding)coincide muy bien con bytearray.decode(encoding)cuando quieres recuperar tu cadena.
Serdalis
66
bytearrayse usa cuando necesita un objeto mutable. No lo necesita para conversiones simples strbytes.
hamstergene
8
@EugeneHomyakov Esto no tiene nada que ver, bytearrayexcepto que los documentos bytesno dan detalles, solo dicen "esta es una versión inmutable de bytearray", así que tengo que citar a partir de ahí.
agf
1
Solo una nota de advertencia de Python in a Nutshell sobre bytes: Evite usar el tipo de bytes como una función con un argumento entero. En v2, esto devuelve el entero convertido a una cadena (byte) porque bytes es un alias para str, mientras que en v3 devuelve una cadena de bytes que contiene el número dado de caracteres nulos. Entonces, por ejemplo, en lugar de los bytes de expresión v3 (6), use el equivalente b '\ x00' * 6, que funciona perfectamente de la misma manera en cada versión.
holdenweb
2
Solo una nota, que si está tratando de convertir datos binarios en una cadena, lo más probable es que necesite usar algo byte_string.decode('latin-1')como utf-8que no cubre todo el rango de 0x00 a 0xFF (0-255), consulte los documentos de Python para más información.
iggy12345
349

Es más fácil de lo que se piensa:

my_str = "hello world"
my_str_as_bytes = str.encode(my_str)
type(my_str_as_bytes) # ensure it is byte representation
my_decoded_str = my_str_as_bytes.decode()
type(my_decoded_str) # ensure it is string representation
hasanatkazmi
fuente
37
Él sabe cómo hacerlo, solo pregunta qué camino es mejor. Por favor, vuelva a leer la pregunta.
agf
30
FYI: str.decode (bytes) no funcionó para mí (Python 3.3.3 dijo que "type object 'str' no tiene atributo 'decode'") en su lugar usé bytes.decode ()
Mike
66
@ Mike: use la obj.method()sintaxis en lugar de la cls.method(obj)sintaxis, es decir, use bytestring = unicode_text.encode(encoding)y unicode_text = bytestring.decode(encoding).
jfs
2
... es decir, está haciendo innecesariamente un método independiente, y luego lo llama pasar selfel primer argumento
Antti Haapala
2
@KolobCanyon La pregunta ya muestra la forma correcta de hacerlo: llamar encodecomo método enlazado en la cadena. Esta respuesta sugiere que en su lugar debería llamar al método independiente y pasarle la cadena. Esa es la única información nueva en la respuesta, y está mal.
abarnert
144

La absolutamente mejor manera es que ninguno de los 2, pero el tercero. El primer parámetro predeterminado es desde Python 3.0. Por lo tanto, la mejor manera esencode 'utf-8'

b = mystring.encode()

¡Esto también será más rápido, porque el argumento predeterminado no da como resultado la cadena "utf-8"en el código C, sino NULLque es mucho más rápido verificar!

Aquí hay algunos horarios:

In [1]: %timeit -r 10 'abc'.encode('utf-8')
The slowest run took 38.07 times longer than the fastest. 
This could mean that an intermediate result is being cached.
10000000 loops, best of 10: 183 ns per loop

In [2]: %timeit -r 10 'abc'.encode()
The slowest run took 27.34 times longer than the fastest. 
This could mean that an intermediate result is being cached.
10000000 loops, best of 10: 137 ns per loop

A pesar de la advertencia, los tiempos fueron muy estables después de repetidas ejecuciones: la desviación fue de solo ~ 2 por ciento.


Usar encode()sin argumento no es compatible con Python 2, ya que en Python 2 la codificación de caracteres predeterminada es ASCII .

>>> 'äöä'.encode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)
Antti Haapala
fuente
2
Aquí solo hay una diferencia considerable porque (a) la cadena es pura ASCII, lo que significa que el almacenamiento interno ya es la versión UTF-8, por lo que buscar el códec es casi el único costo involucrado, y (b) la cadena es pequeña , así que incluso si tuviera que codificar, no habría mucha diferencia. Inténtelo con, por ejemplo, '\u00012345'*10000. Ambos toman 28.8us en mi laptop; presumiblemente, los 50ns adicionales se pierden en el error de redondeo. Por supuesto, este es un ejemplo bastante extremo, pero 'abc'es igual de extremo en la dirección opuesta.
abarnert
@abarnert verdadero, pero aun así, no hay razón para pasar el argumento como una cadena.
Antti Haapala
Según esto, los argumentos predeterminados son siempre "absolutamente la mejor manera" de hacer las cosas, ¿verdad? Este tipo de análisis de velocidad se sentiría como una probable exageración si se tratara de discutir el código C. En un lenguaje interpretado, me deja sin palabras.
hmijail llora a los reenviados el