我试图在Python 3中构建这个bytes对象:

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问题中发现了一些关于向字节添加格式的令人惊讶的消息(另见Python 3字节格式化):

http://bugs.python.org/issue3982

这与bytes(int)返回零这样的奇怪情况的交互更加糟糕

and:

如果字节(int)返回该int的ASCIIfication,对我来说会更方便;但说实话,即使是一个错误也比这种行为要好。(如果我想要这种行为——我从来没有——我宁愿它是一个类方法,像“bytes.zero (n)”那样调用。)

谁能给我解释一下这种行为是怎么来的?


当前回答

有些答案对大数字不适用。

将整数转换为十六进制表示,然后将其转换为字节:

def int_to_bytes(number):
    hrepr = hex(number).replace('0x', '')
    if len(hrepr) % 2 == 1:
        hrepr = '0' + hrepr
    return bytes.fromhex(hrepr)

结果:

>>> int_to_bytes(2**256 - 1)
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'

其他回答

我对范围为[0,255]的单个int的各种方法的性能很好奇,所以我决定做一些定时测试。

根据下面的时间,以及我从尝试许多不同的值和配置中观察到的总体趋势,struct。Pack似乎是最快的,其次是int。To_bytes、bytes和str.encode(不出意外)是最慢的。注意,结果显示了比所表示的更多的变化,并且int。To_bytes和bytes在测试过程中有时会切换速度排名,但是struct。Pack显然是最快的。

在Windows上的CPython 3.7中的结果:

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

int(包括Python2的long)可以使用以下函数转换为字节:

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上工作。

这就是它的设计方式——这是有意义的,因为通常,你会在一个可迭代对象上调用bytes,而不是单个整数:

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

文档声明了这一点,以及字节的文档字符串:

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

来自bytes docs:

因此,构造函数参数被解释为bytearray()。

然后,从bytearray docs:

可选的source参数可以用几种不同的方式初始化数组: 如果它是一个整数,数组将具有该大小,并将初始化为空字节。

注意,它与2不同。X(其中X >= 6)行为,其中bytes是简单的str:

>>> bytes is str
True

PEP 3112:

2.6的str与3.0的bytes类型有很多不同之处;最值得注意的是,构造函数完全不同。

如果问题是如何将一个整数本身(不是它的字符串等效)转换为字节,我认为健壮的答案是:

>>> i = 5
>>> i.to_bytes(2, 'big')
b'\x00\x05'
>>> int.from_bytes(i.to_bytes(2, 'big'), byteorder='big')
5

更多关于这些方法的信息:

https://docs.python.org/3.8/library/stdtypes.html#int.to_bytes https://docs.python.org/3.8/library/stdtypes.html#int.from_bytes