基本数据类型之集合 字符编码
1 集合
1.1 什么是集合?
无序的不重复元素序列称为集合。
1.2 集合的定义
可以使用大括号 { } 或者 set() 定义集合
特征:
- 集合内元素必须为不可变数据类型
- 集合内元素无序
- 集合内元素无重复
s1 = {1, 1, 'aaa', (1, 2)} # {1, (1, 2), 'aaa'}
s2 = set('abc') # {'c', 'b', 'a'}
s3 = set([1, 'a'])
s4 = set([1, ['a']]) # 报错 列表属于可变数据类型
定义空集合必须使用函数set()
{ } 表示定义一个空字典
1.3 集合的使用
使用集合类型时,需要把这个容器作为整体去考虑。
集合不可以通过索引的方式访问元素,因此一般不用于对单个元素进行存取。
一般用于去重操作和关系运算
1.3.1 去重操作
注意:
- 待去重对象中的元素必须为不可变数据类型
- 去重后无法保证原来的顺序
s = 'abcacbcabcba'
print(set(s)) # {'b', 'c', 'a'}
l = ['a', 'b', 'c', 'b', 'a']
print(set(l)) # {'a', 'b', 'c'}
1.3.2 关系运算
基本关系运算包括交,差,并,补等。
1.3.2.1 交集
获取两个或更多集合中都包含的元素
- ‘&’
返回一个新集合-交集 - set.intersection(set1, set2 … etc)
返回一个新集合-交集 - set.intersection_update(set1, set2 … etc)
修改当前集合,返回None
可以理解为在当前集合上移除不重叠的元素
s1 = {1, 2, 3}
s2 = {2, 3, 1, 4}
s3 = {5, 6, 7, 1}
print(s1 & s2 & s3) # {1}
print(s1.intersection(s2, s3)) # {1}
print(set.intersection(s1, s2, s3)) # {1}
print(s1) # {1, 2, 3}
print(s1.intersection_update(s2, s3)) # None
print(s1) # {1}
1.3.2.2 差集
返回的集合元素包含在第一个集合中,但不包含在其它集合
- ‘-’
返回一个新集合-差集 - set.difference(set)
返回一个新集合-差集 - set.difference_update(set)
修改当前集合,返回None,
可以理解为从当前集合中移除这些集合中都存在的元素。
s1 = {1, 2, 3}
s2 = {2, 3, 1, 4}
s3 = {5, 6, 7, 1}
print(s1 - s3) # {1}
print(s1 - s2 - s3) # set()
print(s1.difference(s2)) # {2, 3}
print(set.difference(s1, s3)) # {2, 3}
print(s1) # {1, 2, 3}
print(s1.difference_update(s3)) # None
print(s1) # {2, 3}
1.3.2.3 并集
包含了所有集合的元素,重复的元素只会出现一次
- ‘|’
返回一个新集合-并集 - set.union(set1, set2…)
返回一个新集合-并集 - set.update(set)
修改当前集合,返回None,
可以理解为当前集合添加新的元素
s1 = {1, 2, 3}
s2 = {2, 3, 1, 4}
s3 = {5, 6, 7, 1}
print(s1 | s3) # {1, 2, 3, 5, 6, 7}
print(s1.union(s3)) # {1, 2, 3, 5, 6, 7}
print(set.union(s1, s3)) # {1, 2, 3, 5, 6, 7}
print(s1) # {1, 2, 3}
print(s2.update(s1, s3)) # None
print(s2) # {1, 2, 3, 4, 5, 6, 7}
1.3.2.4 对称差集
返回多个集合中不重复的元素集合
- ‘^’
返回一个新集合-对称差集 - set.symmetric_difference(set)
返回一个新集合-对称差集 - set.symmetric_difference_update(set)
移除当前集合中在其它指定集合中相同的元素,并将其它指定集合中不同的元素插入到当前集合中
s1 = {1, 2, 3}
s2 = {2, 3, 1, 4}
s3 = {5, 6, 7, 1}
print(s1 ^ s3 ^ s2) # {1, 4, 5, 6, 7}
# 这里s1.symmetric_difference()只能接受一个参数
print(s1.symmetric_difference(s3)) # {2, 3, 5, 6, 7}
# 这里set.symmetric_difference()只能接受两个参数
print(set.symmetric_difference(s1, s3)) # {2, 3, 5, 6, 7}
print(s1) # {1, 2, 3}
# 这里s2.symmetric_difference_update()只能接受一个参数
print(s2.symmetric_difference_update(s1)) # None
print(s2) # {4}
1.3.2.5 集合之间的关系
相交、包含、不相交
s1 = {1, 2, 3}
s2 = {2, 3, 4}
s3 = {1, 2, 3}
print(s1.isdisjoint(s2)) # False 判断两个集合是否不相交,即没有相同元素
print(s1.issubset(s2)) # False 判断s1是否是s2的子集
print(s1.issuperset(s2)) # False 判断s1是否是s2的父集
# 判断父子集
print(s1 > s2) # False
print(s1 < s2) # False
print(s1 > s3) # False
print(s1 == s3) # True
print(s1 >= s3) # True
1.3.3 其它操作
- 集合元素个数 len()
s = {'a', 'b', 'c'}
print(len(s)) # 3
- 成员运算 in/not in
- 循环遍历
for s in {1, 2}:
# 代码块
- 删除元素
删除集合中指定的元素
set.discard(item)
返回None,移除一个不存在的元素时不做任何处理
set.remove(item)
返回移除的元素,移除一个不存在的元素时会抛出异常
set.pop()
随机移除一个元素
- 增添
set.add(item)
为集合增添元素
1.4 基本数据类型分类总结
1.4.1 存值数量
- 只能存储一个值(标量,原子类型)
数字,字符串 - 可以存储多个值(容器类型)
列表,元组,字典,集合
1.4.2 访问方式
- 直接访问
只能通过变量名访问整个值:数字 - 顺序访问
可以通过索引访问指定元素,索引对应值,代表顺序,也可以称为序列类型
字符串,列表,元组 - 键访问
可以通过键访问指定元素,索引对应值,无序,也可以称为映射类型
字典
1.4.3 可变/不可变类型
- 可变数据类型
列表,字典,集合 - 不可变数据类型
数字,字符串,元组
2 字符编码
2.1 字符编码简介
文本文件的内容以及python的字符串类型数据等都与字符编码相关
2.1.1 计算机三大核心硬件:CPU,内存,硬盘
- 软件运行前代码及其相关数据存放于硬盘中;
- 软件启动时将硬盘中的数据加载到内存中,CPU从内存中读取相应的指令并执行;
- 软件在运行过程中产生的数据都是先存储于内存中,需要永久保存数据则将数据从内存写入到硬盘中。
2.1.2 软件读取文本文件的流程
文件编辑器读取文本文件的执行流程
- 启动一个文件编辑器
- 文件编辑器会将文件内容从硬盘加载到内存中
- 文本编辑器会将刚刚读入内存中的内容显示到屏幕上
python解释器的执行流程
前两步与文本编辑器相同。
第三步:
python解释器解释执行读入内存中的内容,并开始识别python语法。
2.1.3 字符编码的由来
人类可以识别多种字符,但计算机底层硬件只能识别二进制数字。
因此人类需要将自己的字符翻译成二进制数字再交给计算机。
翻译需要遵循标准,这个标准称为字符编码表,表中存放着人类的字符与数字的对应关系。
2.1.4 常用的字符编码
ASCII
((American Standard Code for Information Interchange) 美国信息交换标准代码
诞生于1967年,主要用于显示现代英语和其他西欧语言,一共包括128个字符,一个字符对应8位二进制数 (8 bit / 1 Bytes)。
GB2312
信息交换用汉字编码字符集 1980年
GBK
汉字编码扩展规范 1995年
一个字符对应16位二进制数 (2 Bytes)
Unicode
统一码、万国码、单一码
- 为每种语言中的每个字符设定了统一且唯一的二进制编码;
- 作为其它编码格式的中转站,与其它编码都有映射关系,因此Unicode编码可以与其它编码相互转换。
一个字符对应16位二进制数 (2 Bytes)
utf-8
Universal Character Set/Unicode Transformation Format
针对Unicode的一种可变长度字符编码,可以降低IO延迟
一个字符对应 1 ~ 3 Bytes
2.1.5 文本文件的读与写
- 存
通过文本编辑器输入文字后,文字转化为Unicode格式的二进制数据存放于内存中,保存时将这些二进制数据写入硬盘中 - 取
将硬盘中Unicode格式的二进制数据加载到内存中,然后通过ASCII表翻译成文字。
内存中使用Unicode编码,可以修改的是存入硬盘中的编码格式,即将内存中的数据从Unicode编码转化成需要的编码格式。
Unicode编码可以与其它编码相互转换,但是其它编码不能通过Unicode编码进行互转。
2.2 文件头
- 读取文件时(解释器)
python3 解释器默认的字符编码为utf-8 (读取)
python2 解释器默认的字符编码为ASCII (读取)
文件头
可以在文件首行指定文件头,修改默认的读取字符编码
# coding: gbk
解释器在读取第一行文件头时使用自己默认的编码,而无论是python3的utf-8还是python2的ASCII,都支持英文和基本符合(#, -等),然后再按照文件头指定的编码格式来读取接下来的文件内容。
- 存储文件时
文本编辑器控制文件按照哪一种字符编码格式进行存储。
核心:文件头指定的编码格式必须与存储文件时的编码格式一致。
2.3 字符串存储格式
对于python解释器的执行流程第三部分,即开始识别语法并执行,当遇到字符串时,也可能出现乱码情况。
t = 'Hello, world.'
变量定义会在内存中开辟空间,并将字符串值存入。
内存中的编码都是unicode编码格式的数据。因此最方便的是将字符串以unicode编码格式存入内存空间中,这样不会出现乱码问题。
在python3中,字符串类型数据默认直接存储为unicode格式,不会出现乱码。
在python2中,按照文件头指定的编码格式来存储字符串类型的值。
如果文件头中没有指定编码,那么python2的解释器会按照默认的编码方式(ASCII)存储。对于中文等非ASCII表中的文字会出现乱码问题。
解决方法:
字符串类型数据前加u,表示将字符串强制以unicode的编码格式进行存储。
s = u'你好。' # 此时即便文件头为utf-8,s的值依然以unicode的编码格式存储
在python3中,字符串以unicode的编码格式进行存储。
2.4 编码与解码
2.4.1 bytes类型
bytes 类型是Python 3中新增加的数据类型,代表一些字节的集合,两个十六进制数构成一个byte,以b开头的字符串属于bytes类型。
2.4.2 编码与解码
- 编码
str.encode(encoding=‘utf-8’)
将字符串从unicode的编码格式转换为指定的编码格式。 - 解码
str.decode(encoding=‘utf-8’)
将字符串从指定的编码格式转换为unicode编码格式。
encode:str --> bytes
decode:bytes --> str
str1 = '你好Hello'
str2 = str1.encode('gbk')
print(str2) # b'\xc4\xe3\xba\xc3Hello' bytes类型
str3 = str2.decode('gbk')
print(str3) # 你好Hello str类型
另一种方式
- 编码 bytes
- 解码 str
data_str = 'Hello, world.'
# str --> bytes
data_bytes = bytes(data_str, encoding='utf-8')
# bytes --> str
data_str = str(data_bytes, encoding='utf-8')
2.5 总结
避免乱码问题
- python3
使用utf-8编码格式保存文件,就什么也不需要做。
否则需要为文件添加文件头声明文件存储的编码方式。 - python2
为文件添加文件头声明文件存储的编码方式
字符串前加u