Подтвердить что ты не робот

Преобразование int в байты в Python 3

Я пытался создать этот байтовый объект в Python 3:

b'3\r\n'

поэтому я попробовал очевидное (для меня) и нашел странное поведение:

>>> bytes(3) + b'\r\n'
b'\x00\x00\x00\r\n'

По-видимому:

>>> bytes(10)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

Мне не удалось увидеть никаких указателей на то, почему преобразование байтов работает таким образом, считывая документацию. Однако в этой проблеме Python я обнаружил некоторые неожиданные сообщения о добавлении format в байты (см. Также Форматирование 3 байта Python):

http://bugs.python.org/issue3982

Это взаимодействует еще хуже с такими странностями, как bytes (int), возвращающими нули сейчас

и

Мне было бы гораздо удобнее, если бы байты (int) вернули ASCII-идентификацию этого int; но, честно говоря, даже ошибка была бы лучше, чем такое поведение. (Если бы я хотел этого поведения, чего у меня никогда не было, я бы предпочел, чтобы это был классный метод, вызывается как "bytes.zeroes(n)".)

Может кто-нибудь объяснить мне, откуда это происходит?

4b9b3361

Ответ 1

То, как оно было спроектировано - и это имеет смысл, потому что обычно вы вызываете bytes в итерабельном вместо одного целого:

>>> bytes([3])
b'\x03'

docs указывают это, а также docstring для bytes:

 >>> help(bytes)
 ...
 bytes(int) -> bytes object of size given by the parameter initialized with null bytes

Ответ 2

С Python 3.2 вы можете сделать

>>> (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: 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')

Соответственно, x == int_from_bytes(int_to_bytes(x)). Обратите внимание, что эта кодировка работает только для беззнаковых (неотрицательных) целых чисел.

Ответ 3

Вы можете использовать struct pack:

In [11]: struct.pack(">I", 1)
Out[11]: '\x00\x00\x00\x01'

" > " - это byte-order (big-endian), а символ "я" - символ . Поэтому вы можете быть конкретным, если хотите сделать что-то еще:

In [12]: struct.pack("<H", 1)
Out[12]: '\x01\x00'

In [13]: struct.pack("B", 1)
Out[13]: '\x01'

Это работает одинаково для обоих python 2 и python 3.

Примечание: обратная операция (байты для int) может быть выполнена с помощью unpack.

Ответ 4

Python 3. 5+ вводит% -interpolation (форматирование printf -style) для байтов:

>>> b'%d\r\n' % 3
b'3\r\n'

См. PEP 0461 - Добавление% форматирования в байты и байтовый массив.

В более ранних версиях вы могли использовать str и .encode('ascii') результат:

>>> s = '%d\r\n' % 3
>>> s.encode('ascii')
b'3\r\n'

Примечание: это отличается от того, что производит int.to_bytes:

>>> n = 3
>>> n.to_bytes((n.bit_length() + 7) // 8, 'big') or b'\0'
b'\x03'
>>> b'3' == b'\x33' != '\x03'
True

Ответ 5

В документации написано:

bytes(int) -> bytes object of size given by the parameter
              initialized with null bytes

Последовательность:

b'3\r\n'

Это символ "3" (десятичный 51) символ "\ r" (13) и "\n" (10).

Следовательно, способ будет рассматривать его как таковой, например:

>>> 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'

Протестировано на IPython 1.1.0 и Python 3.2.3

Ответ 6

ASCIIfication 3 - "\x33" not "\x03"!

Это то, что делает python для str(3), но это было бы совершенно неправильно для байтов, поскольку они должны рассматриваться как массивы двоичных данных и не подвергаться злоупотреблениям как строки.

Самый простой способ достичь желаемого - bytes((3,)), что лучше, чем bytes([3]), потому что инициализация списка намного дороже, поэтому никогда не используйте списки, когда вы можете использовать кортежи. Вы можете преобразовать большие целые числа, используя int.to_bytes(3, "little").

Инициализация байтов с заданной длиной имеет смысл и является наиболее полезной, поскольку они часто используются для создания некоторого типа буфера, для которого вам нужна некоторая память выделенного размера. Я часто использую это при инициализации массивов или расширении какого-либо файла путем записи нулей в него.

Ответ 7

int (включая Python2 long) можно преобразовать в bytes, используя следующую функцию:

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

Обратное преобразование может быть выполнено другим:

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)

Обе функции работают как на Python2, так и на Python3.

Ответ 8

Из байт docs:

Соответственно, аргументы конструктора интерпретируются как для bytearray().

Затем из bytearray docs:

Необязательный параметр источника может использоваться для инициализации массива несколькими способами:

  • Если это целое число, массив будет иметь этот размер и будет инициализирован нулевыми байтами.

Обратите внимание, что это отличается от поведения 2.x(где x >= 6), где bytes просто str:

>>> bytes is str
True

PEP 3112:

2.6 str отличается от типа байтов 3.0s различными способами; в частности, конструктор совершенно другой.

Ответ 9

Поведение происходит из-за того, что в Python до версии 3 bytes был просто псевдоним для str. В Python3.x bytes - неизменяемая версия bytearray - совершенно новый тип, а не обратная совместимость.

Ответ 10

Мне было интересно узнать о производительности различных методов для одного целого в диапазоне [0, 255], поэтому я решил провести некоторые временные тесты.

Исходя из приведенного ниже времени и общей тенденции, которую я наблюдал, когда пробовал много разных значений и конфигураций, struct.pack кажется самым быстрым, за которым следуют int.to_bytes, bytes, а str.encode (что неудивительно) - самое медленное. Обратите внимание, что результаты показывают несколько больше вариаций, чем представлено, и int.to_bytes и bytes иногда переключают ранжирование скорости во время тестирования, но struct.pack явно самый быстрый.

Результаты в CPython 3.7 для 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

Тестовый модуль (с именем int_to_byte.py):

"""Functions for converting a single int to a bytes object with that int value."""

import random
import shlex
import struct
import timeit

def bytes_(i):
    """From Tim Pietzcker answer:
    https://stackoverflow.com/a/21017834/8117067
    """
    return bytes([i])

def to_bytes(i):
    """From brunsgaard answer:
    https://stackoverflow.com/a/30375198/8117067
    """
    return i.to_bytes(1, byteorder='big')

def struct_pack(i):
    """From Andy Hayden answer:
    https://stackoverflow.com/a/26920966/8117067
    """
    return struct.pack('B', i)

# Originally, jfs 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 answer here:
    https://codereview.stackexchange.com/a/210789/140921

    Similar to g10guang 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)'"
        ))

Ответ 11

Хотя предыдущий ответ brunsgaard является эффективной кодировкой, он работает только для целых чисел без знака. Это основано на том, что оно работает для целых чисел со знаком и без знака.

def int_to_bytes(i: int, *, signed: bool = False) -> bytes:
    length = ((i + ((i * signed) < 0)).bit_length() + 7 + 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)

Для кодера вместо (i + ((i * signed) < 0)).bit_length() используется (i + ((i * signed) < 0)).bit_length() i.bit_length() поскольку последнее приводит к неэффективному кодированию -128, -32768 и т.д.

Кредит: CervEd для устранения незначительной неэффективности.

Ответ 12

Если вам не важна производительность, вы можете сначала преобразовать int в str.

number = 1024
str(number).encode()