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

1174

Parece haber dos formas diferentes de convertir una cadena en bytes, como se ve en las respuestas a TypeError: 'str' no es compatible con la interfaz de 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')
9
  • 66
    El uso de codificar / decodificar es más común y quizás más claro. Lennart Regebro 29 de septiembre de 2011 a las 7:39
  • 20
    @LennartRegebro Me despido. Incluso si es más común, leer "bytes ()" sé lo que está haciendo, mientras que encode () no me hace sentir que se está codificando en bytes. m3nda 23/04/2017 a las 5:42
  • 3
    @ 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 24/04/2017 a las 19:26
  • 8
    @LennartRegebro Me siento lo suficientemente bien como para usarlo bytes(item, "utf8"), ya que explícito es mejor que implícito, por lo que ... se establece de forma str.encode( )predeterminada en bytes, lo que lo hace más Unicode-zen pero menos Explicit-Zen. Además, "común" no es un término que me guste seguir. Además,, bytes(item, "utf8")es más como las notaciones str(), y b"string". Mis disculpas si soy tan novato para entender sus razones. Gracias. m3nda 24/04/2017 a las 22:56
  • 5
    @ erm3nda si lee la respuesta aceptada, puede ver que encode()no llama bytes(), es al revés. Por supuesto, eso no es obvio de inmediato y por eso hice la pregunta. Mark Ransom 24/04/2017 a las 23:03
719

Si miras los documentos bytes, te indica lo siguiente bytearray:

bytearray([source[, encoding[, errors]]])

Return a new array of bytes. The bytearray type is a mutable sequence of integers in the range 0 <= x < 256. It has most of the usual methods of mutable sequences, described in Mutable Sequence Types, as well as most methods that the bytes type has, see Bytes and Byte Array Methods.

The optional source parameter can be used to initialize the array in a few different ways:

If it is a string, you must also give the encoding (and optionally, errors) parameters; bytearray() then converts the string to bytes using str.encode().

If it is an integer, the array will have that size and will be initialized with null bytes.

If it is an object conforming to the buffer interface, a read-only buffer of the object will be used to initialize the bytes array.

If it is an iterable, it must be an iterable of integers in the range 0 <= x < 256, which are used as the initial contents of the array.

Without an argument, an array of size 0 is created.

Por byteslo tanto, puede hacer mucho más que 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 lo más autodocumentado - "tome esta cadena y codifíquela con esta codificación" es más claro que bytes(some_string, encoding)- no hay un verbo explícito cuando usa el constructor.

Editar: verifiqué la fuente de Python. Si pasa una cadena Unicode para bytesusar CPython, llama a PyUnicode_AsEncodedString , que es la implementación de encode; por lo que se está saltando un nivel de indirecta si se llama a encodesí mismo.

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

6
  • 98
    +1 por tener un buen argumento y citas de los documentos de Python. También unicode_string.encode(encoding)combina muy bien con bytearray.decode(encoding)cuando quieres que te devuelvan la cuerda. Serdalis 28 de septiembre de 2011 a las 15:30
  • 9
    bytearrayse utiliza cuando se necesita un objeto mutable. No lo necesita para conversiones simples strbytes. hamstergene 28 de septiembre de 2011 a las 15:41
  • 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 28 de septiembre de 2011 a las 15:43
  • 3
    Solo una nota, que si está tratando de convertir datos binarios en una cadena, lo más probable es que necesite usar algo como byte_string.decode('latin-1')ya utf-8que no cubre todo el rango 0x00 a 0xFF (0-255), consulte los documentos de Python para más información. iggy12345 10/07/19 a las 14:25
  • 5
    tl;drsería útiltechkuz 11/12/19 a las 7:46
515

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
11
  • 56
    Sabe cómo hacerlo, solo pregunta cuál es mejor. Vuelva a leer la pregunta. agf 30 de septiembre de 2013 a las 17:50
  • 32
    FYI: str.decode (bytes) no funcionó para mí (Python 3.3.3 dijo "tipo de objeto 'str' no tiene atributo 'decode'") Usé bytes.decode () en su lugarMike 13/08/2014 a las 9:33
  • 6
    @Mike: usa obj.method()sintaxis en lugar de cls.method(obj)sintaxis, es decir, usa bytestring = unicode_text.encode(encoding)y unicode_text = bytestring.decode(encoding). jfs 22/06/2015 a las 11:51
  • 3
    ... es decir, está creando innecesariamente un método no vinculado, y luego lo llama pasando el selfcomo primer argumentoAntti Haapala 11/04/2018 a las 7:41
  • 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, debe llamar al método independiente y pasarle la cadena. Esa es la única información nueva en la respuesta y está mal. abarnert 23/06/18 a las 5:16
207

La absolutamente mejor manera es que ninguno de los 2, pero el tercero. El primer parámetro predeterminado desde Python 3.0. Por tanto, la mejor forma 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 de 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 un 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)
7
  • 2
    Aquí solo hay una diferencia considerable porque (a) la cadena es ASCII puro, 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 , por lo que incluso si tuvieras que codificar, no habría mucha diferencia. Inténtelo con, por ejemplo, '\u00012345'*10000. Ambos toman 28.8us en mi computadora portátil; los 50ns adicionales se pierden presumiblemente en el error de redondeo. Por supuesto, este es un ejemplo bastante extremo, pero 'abc'es igualmente extremo en la dirección opuesta. abarnert 23/06/18 a las 5:22
  • @abarnert es cierto, pero incluso entonces, no hay razón para pasar el argumento como una cadena. Antti Haapala 23/06/18 a las 7:19
  • De acuerdo con 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 exageración probable si se tratara de discutir el código C. En un lenguaje interpretado, me deja sin palabras. hmijail mourns resignees 14 abr.20 a las 23:27
  • 1
    @hmijail, no gana nada escribiendo explícitamente los valores predeterminados de los argumentos: más pulsaciones de teclas, código más grande y también más lento. Antti Haapala 25/07/20 a las 7:16
  • 1
    El Zen de Python declara que explícito es mejor que implícito, lo que significa que 'utf-8'se prefiere un parámetro explícito . Pero definitivamente ha demostrado que dejar el parámetro es más rápido. Eso hace que esta sea una buena respuesta, incluso si no es la mejor. Mark Ransom 7 de nov. De 2020 a las 4:36
26

Respuesta para un problema ligeramente diferente:

Tiene una secuencia de Unicode sin procesar que se guardó en una variable str:

s_str: str = "\x00\x01\x00\xc0\x01\x00\x00\x00\x04"

Necesita poder obtener el byte literal de ese Unicode (para struct.unpack (), etc.)

s_bytes: bytes = b'\x00\x01\x00\xc0\x01\x00\x00\x00\x04'

Solución:

s_new: bytes = bytes(s, encoding="raw_unicode_escape")

Referencia (desplácese hacia arriba para codificaciones estándar):

Codificaciones específicas de Python

3
  • 1
    ¿Por qué intenta responder una pregunta que no se hizo? Seguramente hay otra pregunta en la que esto podría ser útil. Mark Ransom 24 de enero a las 20:07
  • 7
    En realidad, esto era justo lo que estaba buscando. No pude averiguar cómo formular mejor mi pregunta. :) ¡Gracias @Brent! Kade 6 feb a las 18:34
  • 4
    Esta fue la respuesta que necesitaba, proveniente de una búsqueda en Google de "python 3 convertir str a bytes binary", este fue el resultado principal y parecía prometedor. Hay preguntas más interesantes, como cómo convertir una cadena Unicode en una cadena normal (Python 2.7): pOrwellophile 9 feb a las 14:01