【Python】8.有益的探索
作者:互联网
一、概述
尝试着潜入水中,往冰山的深处扎一个小小的猛子
二、数据类型底层实现
1. 从奇怪的列表说起
1.1 错综复杂的复制
list_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "Sarah"}]
- 浅拷贝
# list_3 = list_1 # 错误!!!
list_2 = list_1.copy() # 或者list_1[:] \ list(list_1) 均可实习浅拷贝
- 对浅拷贝前后两列表分别进行操作
输入:
list_2[1].append(55)
print("list_1: ", list_1)
print("list_2: ", list_2)
输出:
list_1: [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'Sarah'}]
list_2: [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'Sarah'}]
1.2 列表的底层实现
引用数组的概念
列表内的元素可以分散的存储在内存中
列表存储的,实际上是这些元素的地址!!!——地址的存储在内存中是连续的
list_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "Sarah"}]
list_2 = list(list_1) # 浅拷贝 与list_1.copy()功能一样
(1)新增元素
输入:
list_1.append(100)
list_2.append("n")
print("list_1: ", list_1)
print("list_2: ", list_2)
输出:
list_1: [1, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 100]
list_2: [1, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 'n']
(2)修改元素
输入:
list_1[0] = 10
list_2[0] = 20
print("list_1: ", list_1)
print("list_2: ", list_2)
输出:
list_1: [10, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 100]
list_2: [20, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 'n']
(3)对列表型元素进行操作
输入:
list_1[1].remove(44)
list_2[1] += [55, 66]
print("list_1: ", list_1)
print("list_2: ", list_2)
输出:
list_1: [10, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah'}, 100]
list_2: [20, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah'}, 'n']
(4)对元组型元素进行操作
元组是不可变的
输入:
list_2[2] += (8,9)
print("list_1: ", list_1)
print("list_2: ", list_2)
输出:
list_1: [10, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah'}, 100]
list_2: [20, [22, 33, 55, 66], (5, 6, 7, 8, 9), {'name': 'Sarah'}, 'n']
(5)对字典型元素进行操作
输入:
list_1[-2]["age"] = 18
print("list_1: ", list_1)
print("list_2: ", list_2)
输出:
list_1: [10, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah', 'age': 18}, 100]
list_2: [20, [22, 33, 55, 66], (5, 6, 7, 8, 9), {'name': 'Sarah', 'age': 18}, 'n']
1.3 引入深拷贝
浅拷贝之后
-
针对不可变元素(数字、字符串、元组)的操作,都各自生效了
-
针对不可变元素(列表、集合)的操作,发生了一些混淆
引入深拷贝
- 深拷贝将所有层级的相关元素全部复制,完全分开,泾渭分明,避免了上述问题
输入:
import copy
list_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "Sarah"}]
list_2 = copy.deepcopy(list_1)
list_1[-1]["age"] = 18
list_2[1].append(55)
print("list_1: ", list_1)
print("list_2: ", list_2)
输出:
list_1: [1, [22, 33, 44], (5, 6, 7), {'name': 'Sarah', 'age': 18}]
list_2: [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'Sarah'}]
2. 神秘的字典
2.1 快速的查找
输入:
import time
ls_1 = list(range(1000000))
ls_2 = list(range(500))+[-10]*5000
start = time.time()
count = 0
for n in ls_2:
if n in ls_1:
count += 1
end = time.time()
print("查找{}个元素,在ls_1列表中的有{}个,共用时{}秒".format(len(ls_2), count,round((end-start),2)))
输出:
查找5500个元素,在ls_1列表中的有500个,共用时39.54秒
输入:
import time
d = {i:i for i in range(100000)}
# print(d)
ls_2 = list(range(500))+[-10]*5000
start = time.time()
count = 0
for n in ls_2:
try:
d[n]
except:
pass
else:
count += 1
end = time.time()
print("查找{}个元素,在ls_1列表中的有{}个,共用时{}秒".format(len(ls_2), count,round(end-start)))
输出:
查找5500个元素,在ls_1列表中的有500个,共用时0秒
2.2 字典的底层实现
通过稀疏数组来实现值的存储与访问
字典的创建过程
- 第一步:创建一个散列表(稀疏数组 N >> n)
d = {}
- 第一步:通过hash()计算键的散列值
输入:
print(hash("python"))
print(hash(1024))
print(hash((1,2)))
输出:
-1700689346824694673
1024
-3550055125485641917
输入:
d["age"] = 18 # 增加键值对的操作,首先会计算键的散列值hash("age")
print(hash("age"))
输出:
3061355840583939350
- 第二步:根据计算的散列值确定其在散列表中的位置
极个别时候,散列值会发生冲突,则内部有相应的解决冲突的办法
- 第三步:在该位置上存入值
for i in range(2, 2):
print(i)
键值对的访问过程
d["age"]
- 第一步:计算要访问的键的散列值
- 第二步:根据计算的散列值,通过一定的规则,确定其在散列表中的位置
-
第三步:读取该位置上存储的值
如果存在,则返回该值
如果不存在,则报错KeyError
2.3 小结
(1)字典数据类型,通过空间换时间,实现了快速的数据查找
- 也就注定了字典的空间利用效率低下
(2)因为散列值对应位置的顺序与键在字典中显示的顺序可能不同,因此表现出来字典是无序的
- 回顾一下 N >> n
如果N = n,会产生很多位置冲突
- 思考一下开头的小例子,为什么字典实现了比列表更快速的查找
3. 紧凑的字符串
通过紧凑数组实现字符串的存储
- 数据在内存中是连续存放的,效率更高,节省空间
- 思考一下,同为序列类型,为什么列表采用引用数组,而字符串采用紧凑数组
4. 是否可变
4.1 不可变类型:数字、字符串、元组
在生命周期中保持内容不变
- 换句话说,改变了就不是它自己了(id变了)
- 不可变对象的 += 操作 实际上创建了一个新的对象
输入:
x = 1
y = "Python"
print("x id:", id(x))
print("y id:", id(y))
输出:
x id: 140718440616768
y id: 2040939892664
输入:
x += 2
y += "3.7"
print("x id:", id(x))
print("y id:", id(y))
输出:
x id: 140718440616832
y id: 2040992707056
元组并不是总是不可变的
当原组中是不可变类型的元素时,原组才是不可变的
输入:
t = (1,[2])
t[1].append(3)
print(t)
输出:
(1, [2, 3])
4.2 可变类型:列表、字典、集合
- id 保持不变,但是里面的内容可以变
- 可变对象的 += 操作 实际在原对象的基础上就地修改
输入:
ls = [1, 2, 3]
d = {"Name": "Sarah", "Age": 18}
print("ls id:", id(ls))
print("d id:", id(d))
输出:
ls id: 2040991750856
d id: 2040992761608
输入:
ls += [4, 5]
d_2 = {"Sex": "female"}
d.update(d_2) # 把d_2 中的元素更新到d中
print("ls id:", id(ls))
print("d id:", id(d))
输出:
ls id: 2040991750856
d id: 2040992761608
5. 列表操作的几个小例子
【例1】 删除列表内的特定元素
- 方法1 存在运算删除法
缺点:每次存在运算,都要从头对列表进行遍历、查找、效率低
输入:
alist = ["d", "d", "d", "2", "2", "d" ,"d", "4"]
s = "d"
while True:
if s in alist:
alist.remove(s)
else:
break
print(alist)
输出:
['2', '2', '4']
- 方法2 一次性遍历元素执行删除
输入:
alist = ["d", "d", "d", "2", "2", "d" ,"d", "4"]
for s in alist:
if s == "d":
alist.remove(s) # remove(s) 删除列表中第一次出现的该元素
print(alist)
输出:
['2', '2', 'd', 'd', '4']
解决方法:使用负向索引
输入:
alist = ["d", "d", "d", "2", "2", "d" ,"d", "4"]
for i in range(-len(alist), 0):
if alist[i] == "d":
alist.remove(alist[i]) # remove(s) 删除列表中第一次出现的该元素
print(alist)
输出:
['2', '2', '4']
【例2】 多维列表的创建
输入:
ls = [[0]*10]*5
ls
输出:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
输入:
ls[0][0] = 1
ls
输出:
[[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
三、更简洁的语法
输入:
输出:
四、三大神器
输入:
输出:
五、重难点回顾
输入:
输出:
标签:Sarah,22,探索,Python,list,有益,ls,print,id 来源: https://blog.csdn.net/weixin_40633696/article/details/115726398