本节我们开始讲python数据类型之bytes类型,我们分为上下两个章节。
你可以直接到这个页面观看本节视频:免费视频教程!零基础学Python系列(7) - 数据类型之bytes(上)
以下为对应的课件内容:
Bytes是python3新增的一个数据类型,用于表示一个字节串,它是一个有序的序列。
通常有两种方式来构造一个bytes类型的对象:
1、通过bytes()函数构造
bytes_1 = bytes('hello', 'utf-8')
bytes_2 = bytes([1, 200, 80, 50])
2、通过b后面跟字符串的方式
bytes_3 = b'world'
bytes_4 = b'\x77\x6f\x72\x6c\x64'
我们在print一个bytes类型数据时,python会以/x的格式依次打印每个字节的值,以两位16进制来显示。但是python对于一些字符会直接字符编码转换,所以造成打印出来的结果看起来很混乱,比如:
bytes_2 = bytes([1, 200, 80, 50])
print('bytes_2:', bytes_2)
输出结果为:
bytes_2: b'\x01\xc8P2'
最后两个数值80、50,被转换为了字符P、2,看起来很混乱。
这时,我们可以写一个简单的方法,让它不做这种转换:
# bytes 按照16进制输出,强制不ascii转码
def trans(s):
return "b'%s'" % ''.join('\\x%.2x' % x for x in s)
bytes_2 = bytes([1, 200, 80, 50])
print('bytes_2:', trans(bytes_2))
输出结果为:
bytes_2: b'\x01\xc8\x50\x32'
这样我们看到,bytes里面包含了一个一个的字节。
因为我们还没有学函数的概念,所以大家只要知道在输出的时候调用这个方法即可。
bytes类型,存储的是一系列的字节,它并不关注这些字节具体表示什么含义(字符、网络数据、图片、音视频等)。Bytes并不约束你如果使用这些字节数据,你可以按照你自己的功能逻辑做任意的转换。这个转换逻辑,不是bytes数据类型的功能范畴。
比如:对于字符,通常我们需要对其做一个编码转换,将字节类型转换为有意义的字符串。这个转换规则,就是字符编码,紧接着下一小节我们会介绍字符编码。
我们可以看到,bytes类型也是一种序列,所以它的大多数操作方法和String一致。
# 操作方法
print(bytes_3[0: 3])
print(bytes_1 + bytes_3)
print(b'h' in bytes_1)
print(bytes_1.split(b'l'))
print(bytes_1.find(b'll'))
print(bytes_1.replace(b'l', b't'))
输出结果为:
b'wor'
b'helloworld'
True
[b'he', b'', b'o']
2
b'hetto'
是不是和string类型高度一致? bytes类型和string类型的对比如下:
- string的基本单位是字符,bytes的基本单位是字节;
- 他们都是属于一种序列,所以对于序列的操作方法,对他们基本都适用;
- String和bytes都是不可变类型,不能对其元素进行修改。
注意,虽然bytes通常会和string一起使用,但是bytes并不只是给string用,它本质上是一个字节串。Bytes适合那种面向二进制流的存储数据,比如图片、视频等多媒体,或者网络通信等二进制报文流。
-
字节序
字节序,顾名思义就是字节存储的顺序。大家可能觉得奇怪,字节不都是“从左到右”依次存储的吗?怎么会有字节序的问题?大家看看下面的例子:
# author: Tiger, wx ID:tiger-python
# file: ./6/6_2.py
# bytes 按照16进制输出,强制不ascii转码
def trans(s):
return "b'%s'" % ''.join('\\x%.2x' % x for x in s)
# 字节序
byte_1 = 'python'.encode('utf-8')
print(trans(byte_1))
print('Big endian: ', hex(int.from_bytes(byte_1, byteorder='big', signed=False)))
print('Little endian: ', hex(int.from_bytes(byte_1, byteorder='little', signed=False)))
输出结果为:
b'\x70\x79\x74\x68\x6f\x6e'
Big endian: 0x707974686f6e
Little endian: 0x6e6f68747970
上面的实例中,我们将bytes类型b’python’强制转换为int类型,在转换过程中分别指定其字节序为big和little。从打印结果可以看出,这两种类型对应的输出结果完全相反。它们对应的就是大端字节序(Big endian,BE)和小端字节序(Little endian,LE)。
比如我要存储一个字节串:b’\x12\x34\x56\x78’:
大端字节序:从低地址到高地址,依次存储数据字节;
小端字节序:相反,从高地址到低地址,依次存储数据字节。因为我们查看内存通常是由低位地址向高位地址看,所以大端字节序是更加符合我们的习惯的,而小端则相反。
为什么计算机会产生两种不同的字节序呢?
因为字节序是由CPU架构决定,而在计算机技术发展初期,CPU架构的两大阵营X86和PowerPC分别采用了完全相反的两种字节序,X86采用了LE,PowerPC采用了BE。所以,才会导致我们现在需要面对字节序的问题。
我们可以下面的方法获取当前cpu的字节序类型:
# 获取当前cpu的字节序类型
import sys
print('endian of cur env:', sys.byteorder)
输出为:
endian of cur env: little
我使用的环境是X86的CPU,对应的是小端字节序。
如果你的程序只会在本地运行,不会涉及到跨主机(跨不同类型CPU)的操作,那么你不需要关注字节序。反之,你需要特别关注字节序,因为它容易出错。
如果计算机A采用了BE架构的CPU,计算机B采用了LE架构的CPU。我们有一段程序,在计算机A发送一个bytes : b’\x12\x34\x56\x78’给计算机B,那么计算机B解析出来的数据将是bytes : b’\x78\x56\x34\x12’,这就完全错了。
在这种跨主机的数据传输中的字节序,我们通常称之为网络字节序,网络字节序和CPU无关,它是网络通信协议定义的一套规范。几乎所有的网络字节序都采用了大端字节序BE。计算机将数据发送给网络协议之前,需要统一转换为网络字节序,同样,接收端的计算机从网络接收到数据后,也会统一将其由网络字节序转换为本机字节序。这样,我们就解决了跨主机的字节序问题。Python的网络编程里面,我们还会涉及到字节序,到时候我们可以回头来看看。
下一节我们继续讲字符编码和string与bytes直接的转换。
本节课程的视频和实例源码下载方式:点击->我的主页,查看个人简介。
我尽量坚持每日更新一节。