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


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

b'3rn'

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

>>> bytes(3) + b'rn'
b'x00x00x00rn'

видимо:

>>> bytes(10)
b'x00x00x00x00x00x00x00x00x00x00'

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

http://bugs.python.org/issue3982

это еще хуже взаимодействует с такими странностями, как байты(int), возвращающие нули теперь

и:

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

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

10 83

10 ответов:

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

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

The документы утверждают это, а также docstring для bytes:

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

из 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):
    return x.to_bytes((x.bit_length() + 7) // 8, 'big')

def int_from_bytes(xbytes):
    return int.from_bytes(xbytes, 'big')

соответственно, x == int_from_bytes(int_to_bytes(x)).

можно использовать структуры:

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

на " > " - это порядок байтов (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) может быть выполнена с распакуйте.

Python 3.5 + вводит % - интерполяцию (printf-стиль форматирования) для байт:

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

посмотреть PEP 0461 -- добавление % форматирования в байты и bytearray.

на более ранних версиях, вы можете использовать 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''
b'\x03'
>>> b'3' == b'\x33' != '\x03'
True

в документации сказано, что:

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

в ASCIIfication из 3-это "\x33" не "\x03"!

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

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

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

int (включая ) можно преобразовать в 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.

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

С bytes docs:

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

затем с bytearray docs:

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

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

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

>>> bytes is str
True

PEP 3112:

2.6 str отличается от типа байтов 3.0 по-разному; в частности, конструктор полностью отличается.

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

number = 1024
str(number).encode()