Преобразование 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 ответов:
вот как он был разработан - и это имеет смысл, потому что обычно, вы бы позвонили
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.xbytes
является неизменяемой версиейbytearray
- совершенно новый тип, не обратно совместимый.
С bytes docs:
соответственно, аргументы конструктора интерпретируются как для ByteArray().
затем с bytearray docs:
необязательный параметр source можно использовать для инициализации массива несколькими различными способами:
- если это целое число, массив будет иметь этот размер и будет инициализирован с нулевыми байтами.
обратите внимание, что отличается от 2.X (где x >= 6) поведение, где
bytes
простоstr
:>>> bytes is str True
2.6 str отличается от типа байтов 3.0 по-разному; в частности, конструктор полностью отличается.