Ich habe versucht, dieses Byte-Objekt in Python 3 zu erstellen:
b'3\r\n'
also versuchte ich das Offensichtliche (für mich) und fand ein komisches Verhalten:
>>> bytes(3) + b'\r\n'
b'\x00\x00\x00\r\n'
Offenbar:
>>> bytes(10)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Ich konnte keine Hinweise darauf finden, warum die Bytes-Konvertierung auf diese Weise funktioniert, und die Dokumentation lesen. Ich habe jedoch in dieser Python-Ausgabe einige Überraschungsnachrichten über das Hinzufügen von format
zu Bytes gefunden (siehe auch Python 3 Byte Formatierung ):
http://bugs.python.org/issue3982
Dies interagiert noch schlechter mit Oddities wie Bytes (int), die jetzt Nullen zurückgeben
und:
Es wäre viel bequemer für mich, wenn bytes (int) die ASCII-Angabe dieses int zurückgibt. Aber ehrlich gesagt wäre sogar ein Fehler besser als dieses Verhalten. (Wenn ich dieses Verhalten wollte - was ich nie habe - wäre ich lieber eine Klassenmethode, die wie "bytes.zeroes (n)" aufgerufen wird.)
Kann mir jemand erklären, woher dieses Verhalten kommt?
So wurde es entworfen - und es ist sinnvoll, da Sie normalerweise bytes
für eine Iteration statt einer einzelnen Ganzzahl aufrufen:
>>> bytes([3])
b'\x03'
Die docs geben dies sowie den docstring für bytes
an:
>>> help(bytes)
...
bytes(int) -> bytes object of size given by the parameter initialized with null bytes
Mit Python 3.2 ist das möglich
>>> (1024).to_bytes(2, byteorder='big')
b'\x04\x00'
https://docs.python.org/3/library/stdtypes.html#int.to_bytes
def int_to_bytes(x):
return x.to_bytes((x.bit_length() + 7) // 8, 'big')
def int_from_bytes(xbytes):
return int.from_bytes(xbytes, 'big')
Dementsprechend x == int_from_bytes(int_to_bytes(x))
.
Sie können das Paket von struct verwenden :
In [11]: struct.pack(">I", 1)
Out[11]: '\x00\x00\x00\x01'
Das ">" ist die Byte-Reihenfolge (Big-Endian) und das "I" ist das Formatzeichen . So können Sie spezifisch sein, wenn Sie etwas anderes tun möchten:
In [12]: struct.pack("<H", 1)
Out[12]: '\x01\x00'
In [13]: struct.pack("B", 1)
Out[13]: '\x01'
Dies funktioniert auf beiden Python 2 und Python 3 gleich.
Hinweis: Die inverse Operation (Bytes in Int) kann mit Unpack durchgeführt werden.
Python 3.5+ führt% -Interpolation (printf
-Stilformatierung) für Bytes ein :
>>> b'%d\r\n' % 3
b'3\r\n'
Siehe PEP 0461 - Hinzufügen von% Formatierung zu Bytes und Bytearray .
In früheren Versionen könnten Sie str
und .encode('ascii')
das Ergebnis verwenden:
>>> s = '%d\r\n' % 3
>>> s.encode('ascii')
b'3\r\n'
Hinweis: Es unterscheidet sich von was int.to_bytes
erzeugt :
>>> n = 3
>>> n.to_bytes((n.bit_length() + 7) // 8, 'big') or b'\0'
b'\x03'
>>> b'3' == b'\x33' != '\x03'
True
Die Dokumentation sagt:
bytes(int) -> bytes object of size given by the parameter
initialized with null bytes
Die Sequenz:
b'3\r\n'
Es ist das Zeichen '3' (dezimal 51) das Zeichen '\ r' (13) und '\ n' (10).
Die Art und Weise würde es als solche behandeln, zum Beispiel:
>>> 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'
Getestet auf IPython 1.1.0 & Python 3.2.3
Die ASCII-Angabe von 3 ist "\x33"
nicht "\x03"
!
Das ist, was Python für str(3)
tut, aber es wäre völlig falsch für Bytes, da sie als Arrays von binären Daten betrachtet werden sollten und nicht als Zeichenketten missbraucht werden sollten.
Der einfachste Weg, um das zu erreichen, was Sie möchten, ist bytes((3,))
. Dies ist besser als bytes([3])
, da das Initialisieren einer Liste viel teurer ist. Verwenden Sie daher niemals Listen, wenn Sie Tupel verwenden können. Sie können größere Ganzzahlen mit int.to_bytes(3, "little")
konvertieren.
Das Initialisieren von Bytes mit einer bestimmten Länge ist sinnvoll und am nützlichsten, da sie häufig zum Erstellen eines Puffertyps verwendet werden, für den Sie Speicher mit einer bestimmten Größe benötigen. Ich verwende dies oft, wenn Sie Arrays initialisieren oder Dateien erweitern, indem Sie Nullen schreiben.
int
(einschließlich long
von Python2) kann mit folgender Funktion in bytes
konvertiert werden:
import codecs
def int2bytes(i):
hex_value = '{0:x}'.format(i)
# make length of hex_value a multiple of two
hex_value = '0' * (len(hex_value) % 2) + hex_value
return codecs.decode(hex_value, 'hex_codec')
Die umgekehrte Konvertierung kann von einer anderen durchgeführt werden:
import codecs
import six # should be installed via 'pip install six'
long = six.integer_types[-1]
def bytes2int(b):
return long(codecs.encode(b, 'hex_codec'), 16)
Beide Funktionen funktionieren sowohl für Python2 als auch für Python3.
Von bytes docs :
Dementsprechend werden Konstruktorargumente wie für bytearray () interpretiert.
Dann von bytearray docs :
Mit dem optionalen source-Parameter können Sie das Array auf verschiedene Arten initialisieren:
- Wenn es sich um eine Ganzzahl handelt, hat das Array diese Größe und wird mit Null-Bytes initialisiert.
Beachten Sie, dass sich das Verhalten von 2.x (wobei x> = 6) unterscheidet, wobei bytes
einfach str
ist:
>>> bytes is str
True
PEP 3112 :
Der 2,6-str-Typ unterscheidet sich vom Bytes-Typ von 3.0 auf verschiedene Weise. vor allem ist der Konstruktor völlig anders.
Das Verhalten kommt von der Tatsache, dass in Python vor Version 3 bytes
nur ein Alias für str
war. In Python3.x ist bytes
eine unveränderliche Version von bytearray
- völlig neuer Typ, nicht abwärtskompatibel.
Ich war neugierig auf die Performance verschiedener Methoden für ein einzelnes Int im Bereich [0, 255]
, also entschied ich mich für einige Timing-Tests.
Basierend auf den unten angegebenen Timings und dem allgemeinen Trend, den ich beim Ausprobieren vieler verschiedener Werte und Konfigurationen beobachtet habe, scheint struct.pack
der schnellste zu sein, gefolgt von int.to_bytes
, bytes
und mit str.encode
(nicht überraschend) der langsamste. Beachten Sie, dass die Ergebnisse mehr Variationen zeigen als dargestellt wird, und int.to_bytes
und bytes
wechselten während des Tests manchmal die Geschwindigkeitsrangfolge, struct.pack
ist jedoch eindeutig die schnellste.
Ergebnisse in CPython 3.7 unter 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
Testmodul (benannt 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)'"
))
Obwohl die vorherige Antwort von brunsgaard eine effiziente Kodierung ist, funktioniert sie nur für vorzeichenlose Ganzzahlen. Diese Funktion baut darauf auf, um sowohl für vorzeichenbehaftete als auch für vorzeichenlose Ganzzahlen zu arbeiten.
def int_to_bytes(i: int, *, signed: bool = False) -> bytes:
length = (i.bit_length() + 7 + int(signed)) // 8
return i.to_bytes(length, byteorder='big', signed=signed)
def bytes_to_int(b: bytes, *, signed: bool = False) -> int:
return int.from_bytes(b, byteorder='big', signed=signed)
# Test unsigned:
for i in range(1025):
assert i == bytes_to_int(int_to_bytes(i))
# Test signed:
for i in range(-1024, 1025):
assert i == bytes_to_int(int_to_bytes(i, signed=True), signed=True)