容器

种类 名称 存储 可变性 结构
字符串 str 存储字符编码 不可变 序列
列表 list 存储变量 可变 序列
元组 tuple 存储变量 不可变 序列
字典 dict 存储键*值对 可变 散列
集合 set 存储键* 可变 散列

*注:能充当键的数据必须是不可变数据类型。

容器的操作

数学运算符

+:用于拼接两个容器。

+=:用原容器与右侧容器拼接,并重新绑定变量。

*:重复生成容器元素。

*=:用原容器生成重复元素, 并重新绑定变量。

< <= > >= == !=:依次比较两个容器中元素,一但不同则返回比较结果。

成员运算符:如果在指定的序列中找到值,返回bool类型。

1
2
数据 in 序列
数据 not in 序列

索引:定位单个容器元素。

1
容器[整数]

正向索引:从0开始,第二个索引为1,最后一个为len(s)-1。

反向索引:从-1开始,-1代表最后一个,-2代表倒数第二个,以此类推,第一个是-len(s)。

切片:定位多个容器元素。

1
容器[开始索引:结束索引:步长]

前闭后开,结束索引不包含该位置元素。

步长是切片每次获取完当前元素后移动的偏移量。

开始、结束和步长默认值分别为 0,-1,1。

序列拆包:多个变量 = 容器。

1
2
a,b,c = tuple03
a,b,c = ["A","B","C"]

需要变量个数等于容器长度。

str 字符串

由一系列字符组成的不可变序列容器,存储的是字符的编码值。

str 类型的字面值:’ ‘、" “、”"" “”"(可见即所得)、’’’ ‘’’。引号冲突的时候可以换着用。

转义字符可以改变字符的原始含义。

1
\'   \"   \n   \\   \t 

原始字符串:取消转义。

1
a = r"C:\newfile\test.py"

字符串格式化就是将一个字符串以某种格式显示。占位符/类型码:%s、%f、%d

1
2
3
4
a = '%s字符串%s' % (str 变量1,str 变量2) # 可以有多个
a = '%.2f字符串' % (float 变量) # 保留两位小数
a = '%.2f字符串' % float 变量 # 只有一个变量的时候可以不要括号
a = '%.2d字符串' % int 变量 # 补全两位整数,像是打印时间

需要注意的是一般来说百分号不需要转义,但如果使用了上面的这个语法,那么百分号是需要转义的,但是格式特殊要用 %%。

除了这种方法,我们还有一种更简便的方法叫做 f-string 的方法可以格式化字符串详情见 f-string 扫盲

list 列表

列表的基础知识

由一系列变量组成的可变序列容器。(字符串是不可变的序列容器)

因为存的是变量,变量是一系列相同长度的地址,所以变化地址是允许的。

1
2
3
4
5
6
7
8
# 创建
列表名 = [元素1, 元素2]
列表名 = [元素1, 元素2, ] # 最后加一个逗号是允许的。
列表名 = list(可迭代对象)

# 增加
列表名.append(追加的元素) # 追加至末尾
列表名.insert(插入位置的索引, 追加的元素) # 追加至索引位置,注意索引为-1时的运行结果

只要是容器就有容器的一般操作,也就是索引和定位。因为列表的索引和定位和一般的容器操作相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查询
data = 列表名 # 传递列表的地址
data = 列表名[0] # 传递第0位数据的地址
data = 列表名[:2] # 传递一个新列表的地址,新列表内包含原列表前两个变量存的地址

# 修改
列表名[替换位置索引] = 替换的元素
列表名[替换位置索引开头: 替换位置索引结尾] = [替换的元素1, 替换的元素2 ...]

# 删除
列表名.remove(数据) # 先循环查找要删除的数据/元素
del 列表名[删除位置索引]
del 列表名[替换位置索引开头: 替换位置索引结尾]

注意:要做什么操作就用什么方法,不要总是想着另辟蹊径。像是下面这种看上去结果相同的操作,其实是不同的。

1
2
3
4
5
6
7
list_re = list(range(5))

print(list_re[-1::-1]) # [4, 3, 2, 1, 0]
print(list(reversed(list_re))) # [4, 3, 2, 1, 0]

# 第一个会直接生成一个列表
# 第二个会先生成迭代器对象,然后转型列表

使用for循环遍历列表中的所有元素。

1
2
for 变量名 in 容器:
变量名是列表的元素

注意:item 和 i 是不同的,遍历容器的时候使用 item 而在计数循环的时候使用 i / index。

列表的复制操作

拷贝:将一份数据复制、备份为两份,一份意外改变不影响另外一份.

浅拷贝 深拷贝
复制第一层数据 复制所有层数据
优点:占用内存较小 缺点:占用内存过多
缺点:深层变化互相影响 优点:数据绝对互不影响
适用性:优先 适用性:深层数据会发生改变
1
2
3
新列表名 = 列表名 # 仅仅复制变量的待拷贝列表的地址
新列表名 = 列表名[:] # 浅拷贝,CPU 寻址一次,第一层数据拷贝/2份,深层数据共享/1份。深层数据互相影响。
新列表名 = copy.deepcopy(列表名) # 复制所有层的数据。占用内存。

接下来我们来看几个例子。

1
2
3
4
list01 = ["北京", "上海", "深圳"]
list02 = list01
list01.insert(0,"天津")
del list01[1]
1
2
3
4
5
list01 = ["北京", "上海"]
list02 = list01
list01[0] = "广东"
list03 = list01[:]
list03[-1] = "深圳"
1
2
3
4
5
import copy
list01 = ["北京",["上海","深圳"]]
list04 = copy.deepcopy(list01)
list04[0] = "北京04"
list04[1][1] = "深圳04"

列表推倒式

英文:List Comprehensions

使用简易方法,将可迭代对象转换为列表。

1
2
3
4
5
变量 = [表达式 for 变量 in 可迭代对象]
变量 = [表达式 for 变量 in 可迭代对象 if 条件]

# 列表推导式嵌套
变量 = [表达式 for 变量1 in 可迭代对象1 for 变量2 in可迭代对象2]

如果if真值表达式的布尔值为False,则可迭代对象生成的数据将被丢弃。

补充内容

list -> str: 填充字符串.join(字符串列表)。其他的方法还有很多,但是我们要注意的是内存的使用。

只有把不可变的数据类型构建成可变的数据类型(list),才能解决对不可变数据(str)进行频繁修改会产生大量的垃圾的问题。

str -> list: list_result = "唐僧,孙悟空,八戒".split(",")。使用一个字符串存储多个信息。

元组

由一系列变量组成的 不可变 序列容器。不可变是指一但创建,不可以再添加/删除/修改元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 创建 -- 容器的基本操作
元组名 = (元素1, 元素2, 元素3)
元组名 = tuple( 可迭代对象 ) # 在可变类型的数据有了结果后,我们可以转成元组,节省空间。

# 2. 定位 -- 容器的基本操作
元组名[索引]

# 3. 遍历 -- 容器的基本操作
for item in tuple01:
代码块

# 4. 特殊
# 注意1:在没有歧义的情况下可以省略小括号。
tuple03 = 10, 20, 30

# 注意2:变量交换操作借助的东西就是元组。
x, y = y, x # 这里的 y,x 就是一个省略了括号的 (y, x) 元组。

# 注意3:如果元组中只有一个元素,必须有逗号。
tuple04 = (10,)

列表和元组最大的区别是内存存储机制的不同,而不是一个可变,一个不可变。

列表 元组
预留内存空间 空间按需分配
内存不够时自动扩容 每次都要开辟新的空间
优点:元素可以变化 缺点:元素不能变化
缺点:内存使用过多 优点:节省内存空间
适用性:针对性使用 适用性:优先

自动扩容:在预留内存空间不够的时候,扩大使用的内存空间。

  1. 开辟一块更大的内存空间。
  2. 拷贝原始列表的数据。
  3. 替换原始列表变量的内存地址。
  4. 副作用:原来的列表被放弃,成为垃圾。

可变和不可变的分类规则是python中类型的顶层分类。

可变 不可变
list 就是 可变的 tuple/int/floar/str/bool 是不可变的
可变的类型操作灵活,能够方便表达 优先使用不可变的,因为占位空间小

注意:元组不能像是列表一样直接使用[ ]推倒式,因为 for 的部分会被当做一个生成器,作为元组的一个元素。如果要使用推倒式类似的形式,我们可以用tuple转型,即 tuple( xxx for x in xxx),把生成器对象转型为元组。

字典

由一系列 键值对 组成的 可变 散列 容器。

散列:对键进行哈希运算,确定在内存中的存储位置,每条数据存储无先后顺序。

序列 散列
有顺序 没有顺序
占用空间小 占用空间大
支持索引切片 定位迅速

键必须唯一且不可变(字符串/数字/元组),值没有限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 创建字典
字典名 = {键1:值1,键2:值2}
字典名 = dict (可迭代对象) # 转换为字典的格式要求:可迭代对象中的元素必须能够"一分为二"。

list01 = ["八戒", ("ts", "唐僧"), [1001, "齐天大圣"]]
dict01 = dict(list01)

# 添加/修改元素:
字典名[键] = 数据 # 键不存在,创建记录。键存在,修改值。

# 获取元素:
变量 = 字典名[键] # 没有键则错误。可以读取数据前,通过 in 判断键在不在。

# 遍历字典:
for 键名 in 字典名: # 遍历字典的键
​ 字典名[键名]

for 值名 in 字典名.values(): # 遍历字典的值
值名

for 键名,值名 in 字典名.items(): # 遍历返回的一个元组的列表。
​ 语句

# 删除元素:
del 字典名[键]

注意:字典不能使用索引和切片操作。

因为字典是根据哈希运算的结果进行存储的,是一种用空间换时间的设计理念。所以在索引的时候相较于其他的容器,字典是 最快的

列表适合储存单一维度的数据,当我们要存储多维度的数据时,我们可以使用字典。

字典推倒式和列表推倒式类似,使用花括号 { } 里面是带冒号的 for 循环。

1
2
3
4
5
6
7
ks = list_ks
vs = list_vs

dict_01 = {ks[i]: vs[i] for i in range(len(ks)) }
dict_02 = {k: v for k,v in dict_01.items()}

dict_01 == dict_02 # True

集合

由一系列不重复的不可变类型变量(元组/数/字符串)组成的可变散列容器。

相当于只有键没有值的字典(键则是集合的数据)。

集合可以去重,而且相较于使用 in 遍历判断,效率极高。哈希计算内存位置,直接判断重复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建
集合名 = {元素1,元素2,元素3}
集合名 = set(可迭代对象)

# 添加
集合名.add(元素)

# 定位
# 因为无序不能使用索引切片。
# 因为不是键值对不能使用键查找键。

# 遍历
for item in set01:
print(item)

# 删除
if 待删数据 in 集合:
集合.remove(待删数据)

集合的数学运算,交集并集补集:

(1) 交集&:返回共同元素。

1
2
3
s1 = {1, 2, 3}
s2 = {2, 3, 4}
s3 = s1 & s2 # {2, 3}

(2) 并集|:返回不重复元素

1
2
3
s1 = {1, 2, 3}
s2 = {2, 3, 4}
s3 = s1 | s2 # {1, 2, 3, 4}

(3) 补集^:返回不同的的元素

1
2
3
s1 = {1, 2, 3}
s2 = {2, 3, 4}
s3 = s1 ^ s2 # {1, 4} 等同于(s1-s2 | s2-s1)

(3) 差-:返回只属于其中之一的元素

1
2
3
s1 = {1, 2, 3}
s2 = {2, 3, 4}
s1 - s2 # {1} 属于s1但不属于s2

判断两个集合之间的关系:

(1) 子集<:判断一个集合的所有元素是否完全在另一个集合中

(2) 超集>:判断一个集合是否具有另一个集合的所有元素

1
2
3
4
s1 = {1, 2, 3}
s2 = {2, 3}
s2 < s1 # True
s1 > s2 # True

(3) 相同或不同== !=:判断集合中的所有元素是否和另一个集合相同。

1
2
3
4
s1 = {1, 2, 3}
s2 = {3, 2, 1}
s1 == s2 # True
s1 != s2 # False

编码

容器中的数据是不可变的。因为在原有基础上修改,有可能破坏其他数据的内存空间。变量可以变化其中的指向信息,原因是地址是固定长度的,不会干扰相邻的数据。

基础编码

  • 字节byte:计算机最小存储单位,等于8 位bit.
  • 字符:单个的数字,文字与符号。
  • 字符集(码表):存储字符与二进制序列的对应关系。
  • 编码ord(字符):将字符转换为对应的二进制序列的过程。
  • 解码chr(编码):将二进制序列转换为对应的字符的过程。

编码方式

  • ASCII编码:包含英文、数字等字符,每个字符1个字节。
  • GBK编码:兼容ASCII编码,包含21003个中文;英文1个字节,汉字2个字节。
  • Unicode字符集:国际统一编码,旧字符集每个字符2字节,新字符集4字节。
  • UTF-8编码:Unicode的存储与传输方式,英文1字节,中文3字节。

注意

  • 代码密度太大会降低代码的复用性,会使功能添加有困难。
  • 在商业项目的时候该加判断就要加判断,防止程序出问题。