web-dev-qa-db-ger.com

Konvertieren von int in Bytes in Python 3

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?

104
astrojuanlu

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
108
Tim Pietzcker

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)).

122
brunsgaard

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.

32
Andy Hayden

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
16
jfs

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

10
Schcriher

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.

5
Bachsau

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.

5
renskiy

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. 

3
alko

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.

3
freakish

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)'"
        ))
2
Graham

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)
0
A-B-B