列表
python列表索引从0开始
创建列表
[1, 2, 3, 4, 5]
列表里面可以容纳各种类型的数据
rhyme = [1, 2, 3, 4, 5, "上山打老虎"]
print(rhyme)
序列,在python中是最常见的数据结构,字符串是一个序列,列表也是一个序列
rhyme = [1, 2, 3, 4, 5, "上山打老虎"]
for each in rhyme:
print(each)
rhyme[0]
rhyme[1]
length = len(rhyme)
rhyme[length - 1]
rhyme[-1] #列表最后一个元素
列表切片
一次性获得多个元素
rhyme = [1, 2, 3, 4, 5, "上山打老虎"]
rhyme[0:3] #前三个元素
rhyme[3:6]
rhyme[:3] #前三个元素
rhyme[3:] #最后三个元素
rhyme[:] #得到整个列表
rhyme[0:6:2] #2----列表步进的跨度值
rhyme[::2]
rhyme[::-2] #倒着来
rhyme[::-1] #列表倒着输出
增
列表跟字符串不同,它并不是说一尘不变的,可以添加元素
append()
在列表的末尾来添加一个指定的元素,但是一次只能添加一个元素
heros = ["钢铁侠","绿巨人"]
heros.append("黑寡妇")
heros
extend()
允许直接添加一个可迭代对象
extend()方法的参数必须是一个可迭代对象,新的内容是追加到原列表最后一个元素的后面
heros = ["钢铁侠","绿巨人","黑寡妇"]
heros.extend(["鹰眼","灭霸","雷神"])
heros
切片
s = [1, 2, 3, 4, 5]
s[len(s):] = [6] #s.append(6)
s # [1, 2, 3, 4, 5, 6]
s[len(s):] = [7, 8, 9] #s.extend([7,8,9])
s # [1, 2, 3, 4, 5, 6, 7, 8, 9]
insert()
在列表的任意位置来添加数据
有两个参数:
一个参数指定待插入的位置
第二个参数指定待插入的元素
s = [1, 3, 4, 5]
s.insert(1, 2)
s #[1, 2, 3, 4, 5]
s.insert(0, 0)
s #[0, 1, 2, 3, 4, 5]
s.insert(len(s), 6)
s #[0, 1, 2, 3, 4, 5, 6]-----append
删
remove()
将指定的元素删除
- 如果列表中存在多个匹配的元素,那么它只会删除第一个
- 如果指定的元素不存在,那么程序就会报错
heros = ["钢铁侠", "绿巨人", "黑寡妇", "鹰眼", "灭霸", "雷神"]
heros.remove("灭霸")
heros
heros.remove("金莲") #ValueError: list.remove(x): x not in list
pop()
删除某个位置上的元素
它的参数就是元素的下标索引值
heros = ["钢铁侠", "绿巨人", "黑寡妇", "鹰眼", "灭霸", "雷神"]
heros.pop(2) #'黑寡妇'
heros # [‘钢铁侠’, ‘绿巨人’, ‘鹰眼’, ‘灭霸’, ‘雷神’]
clear()
清空列表
heros = ["钢铁侠", "绿巨人", "黑寡妇", "鹰眼", "灭霸", "雷神"]
heros.clear()
heros #[]-----空列表
改
列表是可变的,而字符串是不可变的
替换列表中的元素,跟访问列表类似,都是使用下标索引的方法,用赋值运算符就可以将新的值给替换进去了。
heros = ["钢铁侠", "绿巨人", "黑寡妇", "鹰眼", "灭霸", "雷神"]
heros[4] = "蜘蛛侠"
heros # [‘钢铁侠’, ‘绿巨人’, ‘黑寡妇’, ‘鹰眼’, ‘蜘蛛侠’, ‘雷神’]
切片
一步到位,实现多个元素进行替换
heros = ["钢铁侠", "绿巨人", "黑寡妇", "鹰眼", "灭霸", "雷神"]
heros[3:] = ["武松", "林冲", "李逵"]
heros #[‘钢铁侠’, ’绿巨人‘, ‘黑寡妇’, ‘武松’, ‘林冲’, ‘李逵’]
step one: 将赋值号(=)左边指定的内容删除;
step one: 将包含在赋值号(=)右边的可迭代对象中的片段插入左边被删除的位置。
其实它是分上面两个步骤来实现的
sort()
直接实现从小到大排序
eg:对列表里面的元素从小到大进行排序
nums = [3, 1, 9, 6, 8, 3, 5, 3]
nums.sort()
nums #[1, 3, 3, 3, 5, 6, 8, 9]
reverse()
直接实现从大到小排序
原地反转列表中的元素
nums = [3, 1, 9, 6, 8, 3, 5, 3]
nums.sort()
nums #[1, 3, 3, 3, 5, 6, 8, 9]
nums.reverse()
nums #[9, 8, 6, 5, 3, 3, 3, 1]
heros = ["钢铁侠", "绿巨人", "黑寡妇", "武松", "林冲", "李逵"]
heros.reverse()
heros # ['李逵', '林冲', '武松', '黑寡妇', '绿巨人', '钢铁侠']
s.sort(key=None, reverse=False)-----对列表中的元素进行原地排序,key参数用于指定一个用于比较的函数;reverse参数用于指定排序结果是否反转
key:指定排序的算法函数----允许你去定制你想要的排序方式
nums = [3, 1, 9, 6, 8, 3, 5, 3]
nums.sort(reverse=True) # 跟先调用sort再调用reverse效果一样
nums #[9, 8, 6, 5, 3, 3, 3, 1]
查
查找某个列表里有多少个某个元素值
count()
查找某个元素出现的次数
nums = [3, 1, 9, 6, 8, 3, 5, 3]
nums.count(3)
index()
查找某个元素的索引值
如果存在相同的元素,index会返回第一个找到的元素下标值
index(x, start, end)
有两个可选的参数:
start:指定查找的开始位置
end:指定查找的结束位置
heros = ["李逵", "林冲", "武松", "黑寡妇", "绿巨人", "蜘蛛侠"]
heros.index("绿巨人") #4
heros[heros.index("绿巨人")] = "神奇女侠"
heros #['李逵', '林冲', '武松', '黑寡妇', '神奇女侠', '蜘蛛侠']
heros里索引值自动通过index方法来获取,然后对元素进行替换
nums = [3, 1, 9, 6, 8, 3, 5, 3]
nums.index(3) #0
nums.index(3, 1, 7) #5
copy()
用来拷贝一个列表
nums = [3, 1, 9, 6, 8, 3, 5, 3]
nums_copy1 = nums.copy()
nums_copy1 #[3, 1, 9, 6, 8, 3, 5, 3]
切片
拷贝列表
nums = [3, 1, 9, 6, 8, 3, 5, 3]
nums_copy2 = nums[:]
nums_copy2
上述两种拷贝的方法在python中都叫做shallow copy,也就是浅拷贝,python中还有深拷贝(暂不介绍),学习深拷贝要先理解嵌套列表也就是多维列表
列表的加法和乘法
列表的加法------拼接:
要求加号两边都应该也是列表
s = [1, 2, 3]
t = [4, 5, 6]
s + t #[1, 2, 3, 4, 5, 6]
列表的乘法:
重复列表内所有元素若干次
s = [1, 2, 3]
s * 3 #[1, 2, 3, 1, 2, 3, 1, 2, 3]
嵌套列表 nested list
就是在列表里面嵌入一个新的列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] #二维列表
matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
matrix # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
二维列表的直接应用就是举证,计算机图形学、机器人开发、无人驾驶等都是在举证计算的基础上构建起来的
访问嵌套列表
可以使用循环来实现
迭代一个列表使用一层循环,迭代嵌套列表使用相应的嵌套循环
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] #二维列表
for i in matrix:
for each in i:
print(each)
for i in matrix:
for each in i:
print(each, end=' ')
print()
通过下标同样可以访问嵌套列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] #二维列表
matirx[0] #[1, 2, 3]
matrix[0][0] #1
可以通过循环语句来创建并初始化二维列表
A = [0] * 3
A #[0, 0, 0]
for i in range(3):
A[i] = [0] * 3
A #[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
is ---- 同一性运算符
用于检验两个变量是否指向同一个对象的一个运算符
A = [0] * 3
A #[0, 0, 0]
for i in range(3):
A[i] = [0] * 3
A #[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
B = [[0] * 3] * 3
B #[[0, 0, 0], [0, 0, 0], [0, 0, 0]] 但是存在错误
A[1][1] = 1
A #[[0, 0, 0], [0, 1, 0], [0, 0, 0]]
B[1][1] = 1
B #[[0, 1, 0], [0, 1, 0], [0, 1, 0]]
x = "FishC"
y = "FishC"
x is y #True
x = [1, 2, 3]
y = [1, 2, 3]
x is y #False
Python对于不同对象的存储机制是不一样的,比如字符串“FishC”,由于字符串是不能变的,所以它只需要在内存中开辟一个位置来存放就可以了,如果有多个变量名指向同一个字符串
相比起字符串来说,列表则是可变的,python不知道用户什么时候对列表进行改动,所以尽管两个列表是一样的,但是python还是需要为它们开辟两个不同的位置来进行存放
is可以进行追本溯源
A = [0] * 3
A #[0, 0, 0]
for i in range(3):
A[i] = [0] * 3
A #[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
B = [[0] * 3] * 3
B #[[0, 0, 0], [0, 0, 0], [0, 0, 0]] 但是存在错误
A[1][1] = 1
A #[[0, 0, 0], [0, 1, 0], [0, 0, 0]]
B[1][1] = 1
B #[[0, 1, 0], [0, 1, 0], [0, 1, 0]]
A[0] is A[1] #False
A[1] is A[2] #False
B[0] is B[1] #True
B[1] is B[2] #True
B的问题在于试图通过乘号对一个嵌套列表进行拷贝,但其实,它拷贝的只是对同一个列表的引用
对于内嵌的那个列表,它只是拷贝了对其的引用,而非真正地对其进行物理上的拷贝
先创建一个一维列表,再往一维列表里的每一个元素再塞一个列表进去,就变成了二维列表,即嵌套列表
变量不是盒子
变量就是一个名字、一个标签
通过这个变量就可以找到对应的数据
x = [1, 2, 3]
y = x
当赋值运算发生的时候,python并不是将数据放到变量里去,而是将变量跟数据进行挂钩-----引用
将一个变量赋值给另一个变量其实就是将一个变量的引用传递给另一个变量。
x = [1, 2, 3]
y = x
x[1] = 1
x #[1, 1, 3]
y #[1, 1, 3]
如果想要得到两个独立的列表,需要用到拷贝来完成任务
浅拷贝和深拷贝
浅拷贝:
调用列表的copy方法或者使用切片的语法来实现
x = [1, 2, 3]
y = x.copy()
x[1] = 1
x #[1, 1, 3]
y #[1, 2, 3]
copy方法拷贝的是整个列表对象,而不仅仅是变量的引用
切片:
x = [1, 2, 3]
y = x[:]
x[1] = 1
x #[1, 1, 3]
y #[1, 2, 3]
浅拷贝处理一维列表是没有问题的,但是涉及到嵌套列表,则会出现问题
x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
y = x.copy()
x[1][1] = 0
x #[[1, 2, 3], [4, 0, 6], [7, 8, 9]]
y #[[1, 2, 3], [4, 0, 6], [7, 8, 9]]
浅拷贝只是拷贝了外层的对象,如果包含嵌套对象的话,那么拷贝的只是其引用。
要解决这个问题就需要用到深拷贝
深拷贝
要实现深拷贝要借助copy模块,这个模块有两个函数:
copy----实现浅拷贝,实现这个模块要先import导入
deepcopy----深拷贝,deepcopy函数将原对象拷贝的同时,也将对象中所有引用的子对象一并进行了拷贝
x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
y = copy.copy(x) #使用的是copy模块的copy函数----但是实现的都是浅拷贝
x[1][1] = 0
x #[[1, 2, 3], [4, 0, 6], [7, 8, 9]]
y #[[1, 2, 3], [4, 0, 6], [7, 8, 9]]
x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
y = copy.deepcopy(x)
x[1][1] = 0
x #[[1, 2, 3], [4, 0, 6], [7, 8, 9]]
y #[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
如果存在多层嵌套的话,深拷贝也会拷贝每一层嵌套里面的数据
但是默认是使用浅拷贝而不是深拷贝,因为效率问题
列表推导式
eg:已知列表中的元素都是数字,如何将列表中的每一个元素的值都变成原来的2倍?
循环:
oho = [1, 2, 3, 4, 5]
for i in range(len(oho)):
oho[i] = oho[i] * 2
oho #[2, 4, 6, 8, 10]
列表推导式:
oho = [1, 2, 3, 4, 5]
oho = [i * 2 for i in oho]
oho #[2, 4, 6, 8, 10]
列表推导式的效率通常要比循环语句要快上一倍左右的速度,主要因为列表推导式在python解释器里面是以更快的C语言的速度来运行的,因此比使用python脚本的虚拟机pvm里面以步进的速度来运行for循环要快的很多
列表推导式的基本语法:
[expression for target in iterable]
列表推导式的结果一定是一个列表,所以是“[]”,
由于列表推导式的结果是使用一组数据来填充这个列表的,所以需要for语句来搭配,for语句的左侧是一个表达式,相当于一个循环体,经过运算最终才决定存放在列表中的数据。
x = [i for i in range(10)]
x #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
x = [i + 1 for i in range(10)]
x #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
x = []
for i in range(10):
x.append(i+1)
x #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = [c * 2 for c in "FishC"]
y #['FF', 'ii', 'ss', 'hh', 'CC']
ord这个内置函数的作用:就是将单个字符串转换为对应的编码
code = [ord(c) for c in "FishC"]
code #[70, 105, 115, 104, 67]
matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
col2 = [row[1] for row in matrix] #通过for语句获取矩阵的每一行,存放的列表里的是每个row的row[1],即每一行的第二个元素
col2 #[2, 5, 8]
获取矩阵主对角线上的元素
matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
diag = [matrix[i][i] for i in range(len(matrix))]
diag #[1, 5, 9]
diag2 = [matrix[i][2-i] for i in range(len(matrix))] #右下角到左上角的对角线的元素
diag2 #[3, 5, 7]
循环是通过迭代来逐个修改原列表中的元素,
而列表推导式则是直接创建一个新的列表,然后再赋值为原先的这个变量名,二者从根本上来讲是不一样的,但是列表推导式基本能满足需求
A = [0] * 3
A #[0, 0, 0]
for i in range(3):
A[i] = [0] * 3
A #[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
B = [[0] * 3] * 3
B #[[0, 0, 0], [0, 0, 0], [0, 0, 0]] 但是存在错误
A[1][1] = 1
A #[[0, 0, 0], [0, 1, 0], [0, 0, 0]]
B[1][1] = 1
B #[[0, 1, 0], [0, 1, 0], [0, 1, 0]]
A[0] is A[1] #False
A[1] is A[2] #False
B[0] is B[1] #True
B[1] is B[2] #True
利用列表推导式创建嵌套列表
s = [[0] * 3 for i in range(3)]
s #[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
s[1][1] = 1
s #[[0, 0, 0], [0, 1, 0], [0, 0, 0]]
列表推导式其实还可以添加一个用于筛选的if分句
[expression for target in iterable if condition]
even = [i for i in range(10) if i % 2 == 0]
even #[0, 2, 4, 6, 8]
even = [i + 1 for i in range(10) if i % 2 == 0]
even #[1, 3, 5, 7, 9]
顺序就是先执行for语句,再执行if语句,最后才执行左侧的表达式
words = ["Great", "FishC", "Brilliant", "Excellent", "Fantistic"]
fwords = [w for w in words if w[0] == 'F']
fwords #['FishC', 'Fantistic']
列表推导式可以实现嵌套
[expression for target1 in iterable1
for target2 in iterable2
...
for targetN in iterableN ]
嵌套的列表推导式对应嵌套的循环
eg:将二维列表降级为一维列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flatten = [col for row in matrix for col in row]
flatten #[1, 2, 3, 4, 5, 6, 7, 8, 9]
[x + y for x in "fishc" for y in "FISHC"] #['fF', 'fI', 'fS', 'fH', 'fC', 'iF', 'iI', 'iS', 'iH', 'iC', 'sF', 'sI', 'sS', 'sH', 'sC', 'hF', 'hI', 'hS', 'hH', 'hC', 'cF', 'cI', 'cS', 'cH', 'cC']
笛卡尔乘积
_ = [] #如果某个变量是临时的或者无关紧要的,就可以直接用一个下划线当变量名
for x in "fishc":
for y in "FISHC":
_.append(x + y) #['fF', 'fI', 'fS', 'fH', 'fC', 'iF', 'iI', 'iS', 'iH', 'iC', 'sF', 'sI', 'sS', 'sH', 'sC', 'hF', 'hI', 'hS', 'hH', 'hC', 'cF', 'cI', 'cS', 'cH', 'cC']
列表推导式的终极语法:
[expression for target1 in iterable1 if condition1
for target2 in iterable2 if condition2
...
for targetN in iterableN if conditionN ]
[[x, y] for x in range(10) if x % 2 == 0 for y in range(10) if y % 3 == 0] #[[0, 0], [0, 3], [0, 6], [0, 9], [2, 0], [2, 3], [2, 6], [2, 9], [4, 0], [4, 3], [4, 6], [4, 9], [6, 0], [6, 3], [6, 6], [6, 9], [8, 0], [8, 3], [8, 6], [8, 9]]
KISS设计原则-------Keep It Simple & Stupid
简洁胜于复杂,过分复杂的列表推导式会导致后期的阅读和维护代码的成本变得非常高
元组
元组也是一个序列,它既能像列表那样同时容纳多种类型的对象,也拥有字符串不可变的特性
从语法上来讲,元组和列表的差别就是,列表用的是方括号“[]”,元组用的是圆括号“()”
rhyme = (1, 2, 3, 4, 5, "上山打老虎")
rhyme #(1, 2, 3, 4, 5, '上山打老虎')
事实上,元组可以不带括号,只要用逗号隔开就可以了
rhyme = 1, 2, 3, 4, 5, "上山打老虎"
rhyme #(1, 2, 3, 4, 5, '上山打老虎')
跟其他序列类型的对象一样,元组也可以通过下标来获取元素
rhyme = 1, 2, 3, 4, 5, "上山打老虎"
rhyme[0] #第一个元素
rhyme[-1] #最后一个元素
元组是不可变的,试图修改元组内容的行为是不可取的
rhyme[1] = 10 #TypeError: 'tuple' object does not support item assignment
元组也可以进行切片操作
切片的含义是将目标对象中的元素以某种特定的组合导出,而非修改对象本身
rhyme = 1, 2, 3, 4, 5, "上山打老虎"
rhyme[3:] #(4, 5, '上山打老虎')
rhyme[:] #(1, 2, 3, 4, 5, '上山打老虎')
rhyme[::2] #(1, 3, 5)
rhyme[::-1] #('上山打老虎', 5, 4, 3, 2, 1) #这里不是原地进行一个翻转,而是进行切片操作之后导出一个新的对象
查
count()
nums = (3, 1, 9, 6, 8, 3, 5, 3)
nums.count(3) #3
index()
heros = ("蜘蛛侠", "绿巨人", "黑寡妇")
heros.index("黑寡妇") #2
加号(拼接)和乘号(重复)运算符
s = (1, 2, 3)
t = (4, 5, 6)
s + t #(1, 2, 3, 4, 5, 6)-----两个元组拼接一个新的元组
s * 3 #(1, 2, 3, 1, 2, 3, 1, 2, 3)----将元组内容拷贝三次然后生成一个新的元组
元组也可以嵌套
逗号是构成元组的基本条件
s = (1, 2, 3)
t = (4, 5, 6)
w = s, t #嵌套
w #((1, 2, 3), (4, 5, 6))
元组支持迭代
s = (1, 2, 3)
for each in s:
print(each)
s = (1, 2, 3)
t = (4, 5, 6)
w = s, t #嵌套
w #((1, 2, 3), (4, 5, 6))
for i in w:
for each in i:
print(each)
列表推导式同样可以对元组进行一个转换
s = (1, 2, 3, 4, 5)
[each * 2 for each in s] #[2, 4, 6, 8, 10]
(each * 2 for each in s) #<generator object <genexpr> at 0x00000292D8A44C10>-------这个写法生成的不是元组推导式,事实上不存在元组推导式。这种写法叫做生成器
元组圆括号的必要性:一直加上可以避免一些不必要的问题,并且有助于增加代码的可读性
如何生成只有一个元素的元组:
x = (520)
x
type(x) #<class 'int'>
x = (520,)
x
type(x) #<class 'tuple'>-----元组
打包和解包:
生成一个元组,有时候也称为打包
t = (123, "FishC", 3.14) #打包
t #(123, 'FishC', 3.14)
x, y, z = t #解包----将元素赋值给不同的变量
x #123
y #'FishC'
z #3.14
这种行为也适用于任何的序列类型,比如列表
t = [123, "FishC", 3.14] #打包
t #[123, 'FishC', 3.14]
x, y, z = t #解包----将元素赋值给不同的变量
x #123
y #'FishC'
z #3.14
字符串
a, b, c, d, e = "FishC"
a #'F'
b #'i'
c #'s'
d #'h'
e #'C'
无论哪一种序列的解包,都需要注意:
赋值号左边的变量名数量必须跟右侧序列的元素数量一致,否则就会报错
a, b, c = "FishC" #出错----ValueError: too many values to unpack (expected 3)
除非:
a, b, *c = "FishC"
a #'F'
b #'i'
c #['s', 'h', 'C']
python可以多重赋值,背后的实现逻辑就是先通过元组进行打包,再将元组进行解包
x, y = 10, 20
x #10
y #20
_ = (10, 20)
x, y = _
x #10
y #20
元组中的元素虽然是不可变的,但如果元组中的元素是指向一个可变的列表,那么依然可以修改列表里面的内容
s = [1, 2, 3]
t = [4, 5, 6]
w = (s, t)
w #([1, 2, 3], [4, 5, 6])
w[0][0] = 0
w #([0, 2, 3], [4, 5, 6])