欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

python基础 ---编码与解码

程序员文章站 2022-04-18 18:30:26
...

字节序列

bytes和bytearray
bytes是不可变序列,跟字符串类似;bytearray是可变字节数组,类似于列表

编码与解码

计算机硬件层面能识别的只有二进制bit流,操作系统在和硬件交互时需要将字节码转换成二进制bit。
程序是更高级别的语言,能够实现与用户(开发者)交互。而程序与操作系统进行交互时,也需要将相应的程序语言转换成操作系统能识别的格式
而这个转换的过程就会涉及到编码与解码

编码

str --> bytes:将字符串这个字符序列使用指定字符集encode编码为编码为一个一个字节组成的序列bytes

解码

bytes/bytearray --> str:将一个个字节按照某种指定的字符集解码为一个个字符组成的字符串序列

#python缺省字符集为utf-8,也可以手动指定字符集
>>> print("abc".encode())     #默认使用utf-8
>>> print("啊".encode('utf-8'))
>>> print("啊".encode('gbk'))
>>> print(b'abc'.decode('utf8')) #将字节码b'abc'解码为utf-8字符串
>>> print(b'\xb0\xa1'.decode('gbk'))
b'abc'
b'\xe5\x95\x8a'
b'\xb0\xa1'
abc
啊

ASCII码

所有的字符集都会兼容ASCII码:相当于ASCII码是所有除它本身的字符集的真子集

#一些重要的ASCII码
\0x00:ASCII码表中的第一项,C语言中字符串的结束符,十进制的0
\t ,\x09:tab字符
\x0d\x0a:\r\n换行符
\x30~\x39:字符0~9(注意不是数字)
\x41:大写字母A
\x61:小写字母a

bytes初始化

bytes():空bytes
bytes():指定字节的bytes,用0填充,比如bytes(97)就表示指定字符a
bytes(iterable_of_ints) -->bytes组成的字节序列
bytes(string,encoding[,errors]) -->bytes,等价于string.encode()
bytes(bytes_or_buffer) -->从一个字节序列或者buffer复制出一个新的不可变的bytes对象
使用b前缀定义:
	只允许基本的ASCII码字符才可使用b'string'的表示方式
	使用16进制表示b"\x41\x61" --> b'Aa'

bytes类型和str类型相似,操作方式也差不多,其实就可以将bytes类型当做一种特殊的字符串

>>> print(b'abcd'[2]) ##返回相应索引下字符所对应的的ASCII码
99
>>> print(b'\x0d\x0a')
b'\r\n'

>>> bytes(10)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

>>> bytes(range(5))
b'\x00\x01\x02\x03\x04'

bytearray初始化

bytearray():空字节数组
bytearray(int):指定字节的bytearray,用0填充
bytearray(iterable_of_int)  -->bytearray[0,255]组成的可迭代对象
bytearray(string,encoding[,error])  -->类似于string.encode(),不过返回的是可修改对象
bytearray(bytes_or_buffer) -->从一个字节序列或buffer复制出一个新的可变的bytearray对象

bytearray类似于列表,因此很多适用于列表的函数同样也适用于bytearray
append(int):尾部追加一个元素
insert(index,int):在指定索引位置插入元素
extend(iterable_of_int):讲一个可迭代整数集合追加到bytearray尾部
pop(index=-1):从指定索引处删除一个元素,默认从尾部删除
remove(value):找到第一个匹配value的元素然后删除,找不到则报ValueError
clear():清空bytearray
reverse():反转bytearray,就地修改

#int在的范围要在[0,255]内,因为一个字节只有8位,总共包含256中变化
>>> b = bytearray()
>>> b.append(97)
>>> b.append(99)
>>> b.insert(1,98)
>>> b.extend([65,66,67])
>>> b.remove(66)
>>> b.pop()
>>> b.reverse()
>>> print(b)
bytearray(b'Acba')
>>> b.append(100)
>>> print(b)
bytearray(b'Acbad')

线性结构

线性结构的特点

可迭代,iterable
有长度,通过len()来获取长度
有顺序,是一种序列
可通过索引访问
可切片
str,list,tuple,bytes,bytearray都是线性结构

切片

线性结构数据都可以进行切片操作

#切片操作,通过索引来取出部分或全部数据
sequence[start,stop]
sequence[start,stop,step]

同时包含start和stop那么就是前闭后开区间,如果start和stop一样,那么就不会取出任何数据
start,stop,step为整数,可以是正整数,负整数或0
start为0时可以省略
stop为最后一个索引时可以省略
step为1时可以省略
切片时,索引超过右边界就取到结尾,超过左边界就从头部开始取

x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(x[:])  #返回整个列表
print(x[:-1]) #返回整个列表,除了最后一个元素
print(x[0:]) #整个列表
print(x[3:]) #从索引3开始取数据
print(x[3:-1]) #从索引3到最后一个索引,不包含最后一个索引
print(x[9:])
print(x[:9])
print(x[9:-1])
print(x[:100]) #右边界超限,因此直接取到尾部
print(x[-100:]) #左边界超限,从头部开始取
print(x[4:-2]) 
print(x[-4:-2])
print('0123456789'[-4:8])  #67 
print(b'0123456789'[-4:8]) #b'67'
print(bytearray(b'0123456789')[-10:5]) #bytearray(b'01234')

步长step

(1)步长为正整数时,从左向右取数据
(2)步长为负整数时,从右向左取数据,不过这样会生成新的序列然后才取数据,因此会有额外消耗
(3)如果区间的方向和步长的方向不一样,那么就会返回空对象
(4)如果区间的方向和步长的方向一样,就会从起点开始隔步长取数据
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(x[::]) #整个列表
print(x[::2])
print(x[2:8:3])
print(x[:9:3]) #区间为[0,9)
print(x[1::3])
print(x[-10:8:2]) #从头开始取
print(x[2:8:-1]) #区间方向和步长方向相反,因此返回空列表[]

注意看步长和区间的方向

x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(x[-10:]) #All
print(x[-5:6]) #[5]
print(x[-5:-6]) #[]
print(x[6:5]) #这个有点坑,步长默认为正方向
print(x[5:5]) #[]
print(x[1:9:-2])#[]
print(x[::-2]) #不指定区间则不用关注区间和步长方向
print(x[8::-2]) #区间不完整也可以不用关注区间和步长方向
print(x[8:2:-2])#[8,6,4]
print(x[8:-10:2])#[]
print(x[8:-10:-2])#[8,6,4,2]
print(x[-5:4:-1]) #[5]
print(x[-5:5:-1]) #[]

id函数
CPython中返回对象的内存地址可以用来判断是不是同一个对象

注意点:

(1)id函数对临时对象是没有意义的,因为可能对象地址已被清除,然后在同样的地址上新建的对象
(2)比如print函数打印一个临时的列表,打印完成之后,列表的引用次数变成0,那么内存就会释放
(3)但是如果两个同时存在于内存中的对象,如果id得到的地址一样,那么这两个对象一定是同一个对象

x = [0, 1, 2]
y = x[:]   #切片操作会产生新的对象
print(x, y)
print(id(x), id(y))  #所以此处两个列表内容虽然一样,但是地址不一样,因此是两个不同的对象
x[0]=100  #修改列表x不会影响列表y
print(x,y)

引用类型

(1)如果对象中包含引用类型的对象,那么切片也只是复制引用对象的地址
(2)由于切片复制的是引用对象的地址,因此,一旦这个引用对象内容发生改变,同时指向这个地址的两个对象会同时变化
(3)引用对象可以理解成windows中的快捷方式,只要修改某一个快捷方式指向的文件的内容

x = [[1]]
y = x[:]
print(x, y)
print(x == y)   #这个比较的是两个对象的内容
print(id(x), id(y), x is y) #两个对象的地址不同,因此x is y返回False
x[0][0] = 100  #修改x对象中的引用对象(这个只是修改引用对象中的内容,引用对象的地址未发生变化,因此指向此地址的y中的引用对象也会发生变化)
print(x, y)  
print(x == y)  #因为y中的引用对象地址和x中的引用对象地址一致,因此同时发生了改变,所以此时x,y内容仍然一样
print(x is y)
x[0] = 200    #这是将x对象中的引用对象修改成200这个字面常量,完全是另外一个元素,因此这个与y无关
print(x == y) # 此时x,y不再相等
print(x, y)

由此可知,[:]和[::]切片操作其实是一种浅复制,与copy()函数的功能其实是类似的,具体讲解copy函数的可以点此链接浅拷贝与深拷贝

切片赋值

(1)切片赋值直接使用等号来进行
(2)切片操作放在等号的左边
(3)被插入的可迭代对象放在等号的右边
(4)不可变数据类型不可修改

x = [0, 1, 2]
z = 1
z[:] = x # 会报错,整型数据不可修改,字符串,元组也一样,这些数据类型都不能修改

x = [0, 1, 2, 3, 4]
z = list()
z[:] = x
z[1:2] = 10 # 会报错,因为10并不是可迭代对象
z[1:2] = (10,)
z[3:] = (20,)
z[1:] = (40, 50 ,60, 70)
z[1:-1] = ()
print(z)

x = [0, 1, 2, 3, 4]
y = []
y[:] = x
print(x == y) 
print(id(x), id(y), x is y) #x,y内容虽然相等,但是这两个对象并不等价


x = [0, 1, 2, 3, 4]
m=x     #使用等号可以使两个对象同时指向这个列表对象,因此,这两个对象的内存地址都是这个列表对象,因此等价
print(m==x,m is x)
True True