astrojuanlu
Estaba tratando de construir este objeto de bytes en Python 3:
b'3\r\n'
así que probé lo obvio (para mí) y encontré un comportamiento extraño:
>>> bytes(3) + b'\r\n'
b'\x00\x00\x00\r\n'
Aparentemente:
>>> bytes(10)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
No he podido ver ningún indicador sobre por qué la conversión de bytes funciona de esta manera al leer la documentación. Sin embargo, encontré algunos mensajes sorpresa en este problema de Python sobre cómo agregar format
a bytes (ver también formato de bytes de Python 3):
http://bugs.python.org/issue3982
Esto interactúa aún peor con rarezas como bytes (int) que devuelven ceros ahora
y:
Sería mucho más conveniente para mí si bytes(int) devolviera la ASCIIficación de ese int; pero, sinceramente, incluso un error sería mejor que este comportamiento. (Si quisiera este comportamiento, que nunca tuve, preferiría que fuera un método de clase, invocado como “bytes.zeroes(n)”.)
¿Alguien puede explicarme de dónde viene este comportamiento?
brunsgaard
Desde python 3.2 puedes usar to_bytes
:
>>> (1024).to_bytes(2, byteorder="big")
b'\x04\x00'
def int_to_bytes(x: int) -> bytes:
return x.to_bytes((x.bit_length() + 7) // 8, 'big')
def int_from_bytes(xbytes: bytes) -> int:
return int.from_bytes(xbytes, 'big')
Respectivamente, x == int_from_bytes(int_to_bytes(x))
. Tenga en cuenta que la codificación anterior solo funciona para enteros sin signo (no negativos).
Para enteros con signo, la longitud en bits es un poco más complicada de calcular:
def int_to_bytes(number: int) -> bytes:
return number.to_bytes(length=(8 + (number + (number < 0)).bit_length()) // 8, byteorder="big", signed=True)
def int_from_bytes(binary_data: bytes) -> Optional[int]:
return int.from_bytes(binary_data, byteorder="big", signed=True)
-
Si bien esta respuesta es buena, solo funciona para enteros sin signo (no negativos). Lo he adaptado, escribe una respuesta que también funciona para enteros con signo.
– Asclepio
11 de enero de 2019 a las 6:32
-
Eso no ayuda a conseguir
b"3"
desde3
, como dice la pregunta. (Daráb"\x03"
.)– gsnedders
22 de mayo de 2019 a las 14:29
-
Podría valer la pena señalar que ambos
to_bytes
yfrom_bytes
apoyar unsigned
argumento. Esto permite almacenar números tanto positivos como negativos, al costo de un bit adicional.– Señor Miyagi
20 de agosto de 2020 a las 8:25
-
(stackoverflow.com/a/64502258/5267751 explica lo que
+7
es para.)– usuario202729
8 de febrero de 2021 a las 14:25
-
¿Por qué son necesarios los paréntesis y dónde puedo encontrar documentación sobre ellos?
– young_souvlaki
6 abr 2021 a las 19:01
Tim Pietzcker
Esa es la forma en que se diseñó, y tiene sentido porque, por lo general, llamarías bytes
en un iterable en lugar de un solo entero:
>>> bytes([3])
b'\x03'
Él los documentos indican estoasí como la cadena de documentación para bytes
:
>>> help(bytes)
...
bytes(int) -> bytes object of size given by the parameter initialized with null bytes
-
Tenga en cuenta que lo anterior solo funciona con python 3. En python 2
bytes
es solo un alias parastr
lo que significabytes([3])
te dio'[3]'
.– botchniaque
17 de agosto de 2016 a las 13:48
-
En Python 3, tenga en cuenta que
bytes([n])
solo funciona para int n de 0 a 255. Para cualquier otra cosa, planteaValueError
.– Asclepio
21 de diciembre de 2016 a las 6:29
-
@ABB: No es realmente sorprendente ya que un byte solo puede almacenar valores entre 0 y 255.
–Tim Pietzcker
21 de diciembre de 2016 a las 7:15
-
También hay que señalar que
bytes([3])
sigue siendo diferente de lo que quería el OP, es decir, el valor de byte utilizado para codificar el dígito “3” en ASCII, es decir.bytes([51])
cual esb'3'
nob'\x03'
.– Lenz
01/04/2017 a las 21:13
-
bytes(500)
crea una cadena de bytes con len == 500. No crea una cadena de bytes que codifica el número entero 500. Y estoy de acuerdo en quebytes([500])
no puede funcionar, por lo que esa también es una respuesta incorrecta. Probablemente la respuesta correcta esint.to_bytes()
para versiones >= 3.1.– weberc2
20 de junio de 2019 a las 21:57
Puedes usar el paquete de estructura:
In [11]: struct.pack(">I", 1)
Out[11]: '\x00\x00\x00\x01'
El “>” es el orden de bytes (big-endian) y el “yo” es el carácter de formato. Así que puedes ser específico si quieres hacer otra cosa:
In [12]: struct.pack("<H", 1)
Out[12]: '\x01\x00'
In [13]: struct.pack("B", 1)
Out[13]: '\x01'
Esto funciona igual en Python 2 y pitón 3.
Nota: la operación inversa (bytes a int) se puede hacer con deshacer.
-
@AndyHayden Para aclarar, dado que una estructura tiene un tamaño estándar independientemente de la entrada,
I
,H
yB
trabajar hasta2**k - 1
donde k es 32, 16 y 8 respectivamente. Para entradas más grandes planteanstruct.error
.– Asclepio
21 de diciembre de 2016 a las 13:45
-
Presumiblemente votado negativamente ya que no responde a la pregunta: el OP quiere saber cómo generar
b'3\r\n'
es decir, una cadena de bytes que contiene el carácter ASCII “3” y no el carácter ASCII “\x03”– Dave Jones
5 de marzo de 2017 a las 21:17
-
@DaveJones ¿Qué te hace pensar que eso es lo que quiere el OP? Él respuesta aceptada devoluciones
\x03
y la solución si solo quieresb'3'
es trivial La razón citada por ABB es mucho más plausible… o al menos comprensible.–Andy Hayden
5 de marzo de 2017 a las 23:32
-
@DaveJones Además, la razón por la que agregué esta respuesta fue porque Google lo lleva aquí cuando busca hacer precisamente esto. Así que por eso está aquí.
–Andy Hayden
5 de marzo de 2017 a las 23:36
-
Esto no solo funciona igual en 2 y 3, sino que es más rápido que ambos
bytes([x])
y(x).to_bytes()
métodos en Python 3.5. Eso fue inesperado.– Mark Ransom
7 de marzo de 2017 a las 17:03
jfs
Python 3.5+ introduce %-interpolación (printf
-estilo de formato) para bytes:
>>> b'%d\r\n' % 3
b'3\r\n'
Ver PEP 0461 — Agregar % de formato a bytes y bytearray.
En versiones anteriores, podría usar str
y .encode('ascii')
el resultado:
>>> s="%d\r\n" % 3
>>> s.encode('ascii')
b'3\r\n'
Nota: Es diferente de lo que int.to_bytes
produce:
>>> n = 3
>>> n.to_bytes((n.bit_length() + 7) // 8, 'big') or b'\0'
b'\x03'
>>> b'3' == b'\x33' != b'\x03'
True
Schcriher
La documentación dice:
bytes(int) -> bytes object of size given by the parameter
initialized with null bytes
La secuencia:
b'3\r\n'
Es el carácter ‘3’ (51 decimal) el carácter ‘\r’ (13) y ‘\n’ (10).
Por lo tanto, la vía lo trataría como tal, por ejemplo:
>>> bytes([51, 13, 10])
b'3\r\n'
>>> bytes('3', 'utf8') + b'\r\n'
b'3\r\n'
>>> n = 3
>>> bytes(str(n), 'ascii') + b'\r\n'
b'3\r\n'
Probado en IPython 1.1.0 y Python 3.2.3
-
terminé haciendo
bytes(str(n), 'ascii') + b'\r\n'
ostr(n).encode('ascii') + b'\r\n'
. ¡Gracias! 🙂– astrojuanlu
9 de enero de 2014 a las 14:32
-
@Juanlu001, también
"{}\r\n".format(n).encode()
No creo que haya ningún daño al usar la codificación utf8 predeterminada– John LaRooy
12 de febrero de 2015 a las 0:33
La ASCIIficación de 3 es "\x33"
no "\x03"
!
Eso es lo que hace Python para str(3)
pero sería totalmente incorrecto para los bytes, ya que deberían considerarse matrices de datos binarios y no abusarse como cadenas.
La manera más fácil de lograr lo que quieres es bytes((3,))
que es mejor que bytes([3])
porque inicializar una lista es mucho más costoso, así que nunca use listas cuando puede usar tuplas. Puede convertir enteros más grandes usando int.to_bytes(3, "little")
.
La inicialización de bytes con una longitud dada tiene sentido y es la más útil, ya que a menudo se usan para crear algún tipo de búfer para el que necesita alguna memoria asignada de un tamaño determinado. A menudo uso esto cuando inicializo matrices o expando algún archivo escribiéndole ceros.
-
terminé haciendo
bytes(str(n), 'ascii') + b'\r\n'
ostr(n).encode('ascii') + b'\r\n'
. ¡Gracias! 🙂– astrojuanlu
9 de enero de 2014 a las 14:32
-
@Juanlu001, también
"{}\r\n".format(n).encode()
No creo que haya ningún daño al usar la codificación utf8 predeterminada– John LaRooy
12 de febrero de 2015 a las 0:33
mkrieger1
Tenía curiosidad sobre el rendimiento de varios métodos para un solo int en el rango [0, 255]
así que decidí hacer algunas pruebas de tiempo.
Basado en los tiempos a continuación, y en la tendencia general que observé al probar muchos valores y configuraciones diferentes, struct.pack
parece ser el más rápido, seguido de int.to_bytes
, bytes
y con str.encode
(como era de esperar) siendo el más lento. Tenga en cuenta que los resultados muestran algo más de variación de lo que se representa, y int.to_bytes
y bytes
a veces cambió la clasificación de velocidad durante las pruebas, pero struct.pack
es claramente el más rápido.
Resultados en CPython 3.7 en Windows:
Testing with 63:
bytes_: 100000 loops, best of 5: 3.3 usec per loop
to_bytes: 100000 loops, best of 5: 2.72 usec per loop
struct_pack: 100000 loops, best of 5: 2.32 usec per loop
chr_encode: 50000 loops, best of 5: 3.66 usec per loop
Módulo de prueba (llamado int_to_byte.py
):
"""Functions for converting a single int to a bytes object with that int's value."""
import random
import shlex
import struct
import timeit
def bytes_(i):
"""From Tim Pietzcker's answer:
https://stackoverflow.com/a/21017834/8117067
"""
return bytes([i])
def to_bytes(i):
"""From brunsgaard's answer:
https://stackoverflow.com/a/30375198/8117067
"""
return i.to_bytes(1, byteorder="big")
def struct_pack(i):
"""From Andy Hayden's answer:
https://stackoverflow.com/a/26920966/8117067
"""
return struct.pack('B', i)
# Originally, jfs's answer was considered for testing,
# but the result is not identical to the other methods
# https://stackoverflow.com/a/31761722/8117067
def chr_encode(i):
"""Another method, from Quuxplusone's answer here:
https://codereview.stackexchange.com/a/210789/140921
Similar to g10guang's answer:
https://stackoverflow.com/a/51558790/8117067
"""
return chr(i).encode('latin1')
converters = [bytes_, to_bytes, struct_pack, chr_encode]
def one_byte_equality_test():
"""Test that results are identical for ints in the range [0, 255]."""
for i in range(256):
results = [c(i) for c in converters]
# Test that all results are equal
start = results[0]
if any(start != b for b in results):
raise ValueError(results)
def timing_tests(value=None):
"""Test each of the functions with a random int."""
if value is None:
# random.randint takes more time than int to byte conversion
# so it can't be a part of the timeit call
value = random.randint(0, 255)
print(f'Testing with {value}:')
for c in converters:
print(f'{c.__name__}: ', end='')
# Uses technique borrowed from https://stackoverflow.com/q/19062202/8117067
timeit.main(args=shlex.split(
f"-s 'from int_to_byte import {c.__name__}; value = {value}' " +
f"'{c.__name__}(value)'"
))
-
@ABB Como mencioné en mi primera oración, solo estoy midiendo esto para un solo int en el rango
[0, 255]
. Supongo que por “indicador incorrecto” quiere decir que mis medidas no eran lo suficientemente generales para adaptarse a la mayoría de las situaciones. ¿O mi metodología de medición era deficiente? Si es lo último, me interesaría escuchar lo que tiene que decir, pero si es lo primero, nunca afirmé que mis medidas fueran genéricas para todos los casos de uso. Para mi situación (quizás de nicho), solo estoy tratando con enteros en el rango[0, 255]
, y esa es la audiencia a la que pretendía dirigirme con esta respuesta. ¿Mi respuesta no fue clara? Puedo editarlo para mayor claridad…–Graham
11 de enero de 2019 a las 12:19
-
¿Qué pasa con la técnica de simplemente indexar una codificación precalculada para el rango? El cálculo previo no estaría sujeto a tiempos, solo la indexación lo estaría.
– Asclepio
11 de enero de 2019 a las 15:29
-
@ABB Esa es una buena idea. Eso suena como que será más rápido que cualquier otra cosa. Haré algo de tiempo y lo agregaré a esta respuesta cuando tenga algo de tiempo.
–Graham
11 de enero de 2019 a las 19:03
-
Si realmente desea cronometrar los bytes desde iterables, debe usar
bytes((i,))
en vez debytes([i])
porque las listas son más complejas, usan más memoria y tardan más en inicializarse. En este caso, para nada.– Bachsau
28 de marzo de 2019 a las 6:28
No queda claro a partir de su pregunta si desea el valor entero 3 o el valor del carácter ASCII que representa el número tres (valor entero 51). El primero es bytes ([3]) == b’\x03′. El último es bytes ([ord(‘3′)]) == b’3’.
– florisla
5 de abril de 2017 a las 6:56
Qué hay de malo en:
("3" + "\r\n").encode()
?– GLRoman
27 de agosto de 2020 a las 16:34