说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
Python 操作二进制数据的核心内置类型是字节串(bytes)和字节数组(bytearray)。它们都支持内存视图(memoryview),使用缓冲协议访问其他二进制对象的内存,而无需复制产生新的对象。它们都是序列类型,其中字节数组(bytearray)是可变类型。
注:这里是相关概念的讲解,不是 Python 的实现
0 和 1 是计算机工作的根本,单个的0和1只能表达两种状态,无法满足我们复杂的计算,于是计算机使用了8位即一个 byte 作为一个储存的基本单位。字节(Byte )是计算机信息技术用于计量存储容量的一种计量单位,作为一个单位来处理的一个二进制数字串,是构成信息的一个小单位。即:
以下是一个16位(两个字节)的字符:
bytes 和 bytearray 区别:
bytes 和 字符串区别:
它们之间的映射被称为编码/解码。在 Python 中,程序中的文本都用字符串表示,在执行时转换为字节码执行。
Python 的 bytes 对象是由若干个单字节构成的不可变序列。 由于许多主要二进制协议都基于 ASCII 文本编码,因此 bytes 对象供支持 ASCII 数据,并且在许多特性上与字符串对象相关方法相同。
想定义一个字节串和字节数组序列,可以通过字面值和内置函数来定义:
b'a' # b 前缀,引号中全为 ASCII 的形式,引号可为单、双、三引号
b'abc123554666588756' # 长度理论没有限制
b'我' # yntaxError: bytes can only contain ASCII literal characters
br'abc' # 禁止转义序列处理
rb'abc' # 同上
# 限制为 0 <= x < 256
bytes(10) # 指定长度的以零值填充的 bytes 对象
# b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
bytes(range(20)) # 通过由整数组成的可迭代对象
# b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b...'
obj = (1, 2)
bytes(obj) # 通过缓冲区协议复制现有的二进制数据
# b'\x01\x02'
bytes 字面值中只允许 ASCII 字符(无论源代码声明的编码为何),任何超出 127 的二进制值必须使用相应的转义序列形式加入 bytes 字面值。
详情可参考 bytes() 内置函数。
bytearray 对象是 bytes 对象的可变对应对象,使用 bytearray() 内置函数创建:
bytearray() # 创建一个空实例
# bytearray(b'')
bytearray(10) # 创建一个指定长度的以零值填充的实例
# bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
bytearray(range(20)) # 通过由整数组成的可迭代对象
bytearray(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b...')
bytearray(b'Hi!') # 通过缓冲区协议复制现有的二进制数据
# bytearray(b'Hi!')
详情可参考 bytearray() 内置函数。
bytes 只负责以字节序列的形式(二进制形式)来存储数据,至于这些数据到底表示什么内容(字符串、数字、图片、音频等),完全由程序的解析方式决定。如果采用合适的字符编码方式(字符集),字节串可以恢复成字符串;反之亦然,字符串也可以转换成字节串。
bytes 只是简单地记录内存中的原始数据,至于如何使用这些数据,bytes 并不在意,你想怎么使用就怎么使用,bytes 并不约束你的行为。
bytes 类型的数据非常适合在互联网上传输,可以用于网络通信编程;bytes 也可以用来存储图片、音频、视频等二进制格式的文件。
用 bytes 和 bytearray 可以非常方便的以字节为单位处理数据,比如网络中接收到的原始数据。
一个典型的场景,在读取 IO 数据流的时候,由于 bytes 不可变(无法追加),我们可以使用 bytearray。因用 bytes 接收数据流,每次读取一段内容都会生成一个新的对象,每次都需要重新分配内存。而使用 bytearray 等可变对象接收数据流,因为可变对象一般都有内存分配机制,每次分配内存时都会多分配一点,因此可以减少内存的分配次数。
bytes 和 bytearray 之间,以及它们和其他基础数据类型可以进行转换。bytes 和 bytearray 之间的转换:
b = b'abc'
ba = bytearray(b) # bytearray(b'abc')
bytes(ba) # b'abc'
其他类型转为 bytes 和 bytearray:
s = 'hello'
b = s.encode()
b
# b'hello'
ba = bytearray(s.encode())
ba
# bytearray(b'hello')
i = 6
bytes(i)
# b'\x00\x00\x00\x00\x00\x00'
bytearray(i)
# bytearray(b'\x00\x00\x00\x00\x00\x00')
t = (1, 2, 3)
bytes(t)
# b'\x01\x02\x03'
bytearray(t)
# bytearray(b'\x01\x02\x03')
ser = pd.Series([1,2,3])
bytes(ser) # b'\x01\x02\x03'
bytearray(ser) # bytearray(b'\x01\x02\x03')
bytes 和 bytearray 转为其他类型:
b = b'123'
str(b) # "b'123'"
str(b, encoding='utf-8') # '123'
int(b) # 123
float(b) # 123.0
b = bytearray(b'123')
str(b) # "bytearray(b'123')"
str(b, encoding='utf-8') # '123'
int(b) # 123
float(b) # 123.0
bytes 和 bytearray 本质上依然是序列,与 list 或者 string 类似,也支持切片操作。Python 中的切片会创建一个完整的副本,比如 list[:5] 会创建一个新的 list 对象,包含了前 5 项数值。如果我们现在从一个文件中读取了一些数据,经过处理认为只需要将这些数据的前 10 字节保存起来,经典的做法是将 data[:10] 切片写入文件。正如前面提到的,切片的副本是完全可以避免的,这就可以借助 memoryview 获得更高效的实现。
memoryview 为内存视图,也可以理解为代理,通过这个代理操作内存对象时,不会触发内存的复制,也就是不会生成新的对象。
可以内置函数 memoryview() bytes-like 对象创建,object 必须支持缓冲区协议,支持缓冲区协议的内置对象有 bytes 和 bytearray 。
如,我们知道字符串的切片操作是会生成新的对象的,也就是触发了内存的复制:
s1 = b'abc'
s2 = s1[:] # 新的对象
如果我们通过memoryview 这个代理来操作切片,就不会生成新的对象:
b = b'abc'
b_proxy = memoryview(b)
b2 = b_proxy[:]
b2.tobytes()
# b'abc'
如果你需要把很长的字符串或数据流传递给函数作为参数,我觉得可以使用 memoryview 来提高性能。
bytes, bytearray 和 memoryview 在逻辑上可以类比以下 C 描述:
// bytes
char * data = "hello world";
// bytearray
char data[12] = "hello world";
// memoryview
char *p = data;
作为序列类型,支持序列类型的通用操作,与字符串类似,也支持类似字符串的一些操作,但也一些自己的特殊方法。
todo
查看 utf-8 编码中英文字节占用量:
c = '我'.encode()
c
# b'\xe6\x88\x91'
type(c)
# bytes
len(c)
# 3
# 解码
c.decode()
# '我'
e = 'H'.encode()
e
# b'H'
type(e)
# bytes
len(e)
# 1
todo
更新时间:2022-04-19 14:18:58 标签:python 字节串 二进制 bytes bytearray memoryview