出现以下问题是因为我试图使用
bytes
字符串作为字典键,而我理解为相等的字节值并未被视为相等。
为什么以下 Python 代码比较不相等 - 这两个不是相同二进制数据的等效表示(该示例是故意选择的以避免字节序)?
b'0b11111111' == b'0xff'
我知道以下评估结果为真,证明了等价性:
int(b'0b11111111', 2) == int(b'0xff', 16)
但是为什么 Python 强迫我知道表示形式呢?与字节顺序有关吗?除了将它们全部转换为例如十六进制文字之外,是否有一些简单的方法可以强制它们比较等效值?是否有一种透明且清晰的方法可以以(某种程度上)独立于平台的方式在所有表示之间移动(或者我要求太多)?
假设我实际上想使用 8 位以
b'0b11111111'
的形式对字典进行索引,那么为什么 Python 将其扩展为 10 个字节以及如何防止这种情况发生?
这是大型树数据结构的一小部分,将索引扩展 80 倍似乎是对内存的巨大浪费。
字节可以代表任意数量的事物。 Python 不能也不会猜测你的字节可能编码的内容。
例如,
int(b'0b11111111', 34)
也是一个有效的解释,但该解释不等于十六进制FF。
事实上,解释的数量是无穷无尽的。这些字节可以表示一系列 ASCII 代码点、图像颜色或音符。
在您明确应用解释之前,字节对象仅由 0-255 范围内的值序列组成,并且这些字节的文本表示形式使用 ASCII(如果可表示为可打印文本):
>>> list(bytes(b'0b11111111'))
[48, 98, 49, 49, 49, 49, 49, 49, 49, 49]
>>> list(bytes(b'0xff'))
[48, 120, 102, 102]
那些字节序列不相等。
如果您想将这些序列显式解释为整数文字,请使用
来解释decoded 文本值;在比较之前始终先标准化:
>>> import ast
>>> ast.literal_eval(b'0b11111111'.decode('utf8'))
255
>>> ast.literal_eval(b'0xff'.decode('utf8'))
255
b'0b11111111'
由10个字节组成:
In [44]: list(b'0b11111111')
Out[44]: ['0', 'b', '1', '1', '1', '1', '1', '1', '1', '1']
而
b'0xff'
由 4 个字节组成:
In [45]: list(b'0xff')
Out[45]: ['0', 'x', 'f', 'f']
显然,它们不是同一个对象。
Python 重视明确性。 (
显式优于隐式。)它并不假设b'0b11111111'
必然是整数的二进制表示。它只是一串字节。必须明确说明您选择如何解释它。
0b11111111
(或 255)的字节字符串。这不是
b'0b11111111'
所做的 – 它实际上代表表示 character(Unicode) 字符串
'0b11111111'
的字节字符串。你想要的就写成
b'\xff'
。您可以检查它实际上是一个字节:
len(b'\xff') == 1
。要将 Python
int
转换为二进制表示形式,您可以使用
ctypes
库。您需要选择一种 C 整数类型,例如:
>>> bytes(ctypes.c_ubyte(255))
b'\xff'
>>> bytes(ctypes.c_ubyte(0xff))
b'\xff'
>>> bytes(ctypes.c_long(255))
b'\xff\x00\x00\x00\x00\x00\x00\x00'
注意:您可以分别使用别名
c_ubyte
(即 8 位无符号 C 整数)和
c_long
(64 位有符号 C 整数)来代替 c_uint8
和 c_int64
。转换回来:
>>> ctypes.c_ubyte.from_buffer_copy(b'\xff').value
255
小心溢出:
>>> ctypes.c_ubyte(256)
c_ubyte(0)
byte
类型。您有
bytes
和 bytearray
,分别用于可变和不可变数据。这意味着虽然 0b1111_1111
和
0xff
代表相同的整数值 255,但使用 b'0b1111_1111'
只是一串 ASCII 字符,在本例中为 11 个字符。但也许,你的意思更多的是:
>>> bytes([0xff])
b'\xff'
>>> bytes([0b1111_1111])
b'\xff'
>>> bytes([0xff]) == bytes([0b1111_1111])
True