0.可哈希/不可哈希
可哈希 ( hashable ) 即不可变的数据结构
不可哈希 ( unhashable ) 即可变的数据结构
哈希作用 : 是一个将大体量数据转化为很小数据的过程,
在固定的时间与复杂度下查询它 , 哈希对高效的算法和数据结构很重要。
1.可变/不可变
七个标准数据类型中:
不可变数据类型:整型int、浮点型float、字符串str、元组tuple
可变数据类型 : 列表list、 字典dicr、 集合set
基于内存地址来说:
改变数据,id没变,就是可变类型。 (修改原来的值)
改变数据,id改变,就是不可变类型。 (产生新的值)
1.2不可变类型验证
Python中,定义不可变类型时,会在已经定义的对象中寻找对象是否被声明过,
如果对象已经声明过就直接指向该对象,引用计数加 1 ,不会在申请新的内存。
如果改了变量的值,就相当于新建了一个对象,绑定新的对象。
a = 1
b = 1
print ( id ( a) , id ( b) )
a = 2
print ( id ( a) , )
x = 3.1
print ( id ( x) )
x = 3.2
print ( id ( x) )
"""
1955853640064
1955853640424
"""
x = "abc"
print ( id ( x) )
x = 'gggg'
print ( id ( x) )
"""
2601477377528
2601477456536
"""
"""
t1 = (1, 2, 3)
t1[0] = 1
TypeError:“tuple”对象不支持项分配
"""
1.3可变类型验证
Python中,定义可变类型时,定义同样内容的变量,会重新申请内存地址进行存储。
如果使用方法操作,变量的值发生改变,内存地址是不会变的。(除非变量重新赋值)
列表修改元素的值,变量指向这索引空间没改,只是让索引重新指向一个新的内存地址。
列表list 字典dict 为可变类型 , 也都是容器类型 .
list1 = [ 'aaa' , 'bbb' , 'ccc' ]
print ( id ( list1) )
list1[ 0 ] = 'AAA'
print ( list1)
print ( id ( list1) )
"""
1325891089672
['AAA', 'bbb', 'ccc']
1325891089672
"""
dic = { 'k1' : 111 , 'k2' : 222 }
print ( id ( dic) )
dic[ 'k1' ] = 3333333333
print ( dic)
print ( id ( dic) )
"""
1793239671288
{'k1': 3333333333, 'k2': 222}
1793239671288
"""
# 集合不好演示
1.4补充
关于字典补充:
其中value可以是任意类型
但是key必须是不可变类型
关于元组的补充:
如果元组的元素 ( 可变类型都是容器类型 ) 是可变类型 , 那个这个容器类型的内的元素可以被修改 .
t1 = ( [ 1 ] , 2 , 3 )
t1[ 0 ] [ 0 ] = 'a'
print ( t1)
"""
(['a'], 2, 3)
"""
1.5使用方法
可变类型使用内置方法会产生一个新的值 . ( 使用新的变量去接收这个值 )
不可变类型使用内置方法后会直接修改值 .
str1 = 'hello'
print ( str1. split( ) )
print ( str1)
str2 = str1. split( )
print ( str2)
list1 = [ 'hello' , ]
print ( list1. clear( ) )
print ( list1)
list2 = list1. clear( )
print ( list2)
2.深/浅拷贝
2.1复制列表
需求:
1. 拷贝一下原列表产生一个新的列表
2. 想让两个列表完全独立开,并且针对的是改操作的独立而不是读操作
* 意思就是读怎么复制都不会问题的 , 问题出现再修改上 , 修改一个列表另一个列表不受影响 ! *
2.2直接赋值
直接赋值:当创建一个对象list1,然后把它赋给另一个变量list2 的时候,
Python并没有拷贝这个对象,而只是拷贝了这个对象的引用 ,
原始列表list1改变,被赋值的list1也会做相同的改变 .
list1 = [ 'a' , [ 1 , 2 ] ]
list2 = list1
print ( list1, id ( list1) )
print ( list2, id ( list2) )
"""
list1 与 list2 都指向同一个内存地址.
['a', [1, 2]] 1656102548744
['a', [1, 2]] 1656102548744
"""
list1[ 0 ] = 1
print ( list1, id ( list1) )
print ( list2, id ( list2) )
"""
list1 与 list2 都指向同一个内存地址. list1改变, list2特跟着改变.
[1, [1, 2]] 1592099763464
[1, [1, 2]] 1592099763464
"""
2.3浅拷贝
浅拷贝 : 把原列表第一层内存地址不加与区分玩全拷贝一份 .
list1 = [ 'a' , [ 1 , 2 ] ]
list2 = list1. copy( )
print ( list1, id ( list1) , id ( list1[ 0 ] ) , id ( list1[ 1 ] ) )
print ( list2, id ( list2) , id ( list2[ 0 ] ) , id ( list2[ 1 ] ) )
"""
# 内存地址不一样,存的值是同一个地址
['a', [1, 2]] 2638936700168 2638933860168 2638936700232
['a', [1, 2]] 2638936700040 2638933860168 2638936700232
"""
list1[ 0 ] = 1
print ( list1, id ( list1) , id ( list1[ 0 ] ) , id ( list1[ 1 ] ) )
print ( list2, id ( list2) , id ( list2[ 0 ] ) , id ( list2[ 1 ] ) )
"""
不可变类型的值修改,不会影响另一个列表
[1, [1, 2]] 2638936700168 1602906880 2638936700232
['a', [1, 2]] 2638936700040 2638933860168 2638936700232
"""
list1[ 1 ] [ 0 ] = 'a'
print ( list1, id ( list1) , id ( list1[ 0 ] ) , id ( list1[ 1 ] ) )
print ( list2, id ( list2) , id ( list2[ 0 ] ) , id ( list2[ 1 ] ) )
"""
可变类型的值修改,影响另一个列表
[1, ['a', 2]] 2638936700168 1602906880 2638936700232
['a', ['a', 2]] 2638936700040 2638933860168 2638936700232
"""
对于不可变类型的赋值 , 都是产生了新的值 , ( lsit1 ) 列表索引重新断开旧值绑定新值 .
索引指向的内存地址发送改变 .
对于可变类型中包含的值 , ( list1 ) 列表的索引指向仍然指向原来索引 , 索引的内存地址没变,
变的是索引指向的值 , 于是新列表也跟着一起改变 .
2.4深拷贝
要想拷贝得到的新列表与原列表的改操作完全独立开 .
必须有一种可以区分开可变类型与不可变类型的拷贝机制,这就是深拷贝 .
import copy
list1 = [ 'a' , [ 1 , 2 ] ]
list2 = copy. deepcopy( list1)
print ( list1, id ( list1) , id ( list1[ 0 ] ) , id ( list1[ 1 ] ) , id ( list1[ 1 ] [ 0 ] ) , id ( list1[ 1 ] [ 1 ] ) )
print ( list2, id ( list2) , id ( list2[ 0 ] ) , id ( list2[ 1 ] ) , id ( list2[ 1 ] [ 0 ] ) , id ( list2[ 1 ] [ 1 ] ) )
"""
变量id不一样 lis t[1]存不可变类型值值 list [1] 可变类型 存的值 存的值
['a', [1, 2]] 1977295991944 1977288826696 1977295992072 1602906880 1602906912
['a', [1, 2]] 1977295965768 1977288826696 1977295964808 1602906880 1602906912
"""
不可变类型不修改值的情况下还是一个地址 , 可变类型不区分索引 .
list1[ 0 ] = 1
print ( list1)
print ( list2)
"""
修改不可变类型,互不影响
[1, [1, 2]]
['a', [1, 2]]
"""
list1[ 1 ] [ 0 ] = 'a'
print ( list1)
print ( list2)
"""
修改可变类型,互不影响
[1, ['a', 2]]
['a', [1, 2]]
"""
修改值互不影响 .
2.5总结
只针对可变类型数据 :
赋值的方式 : id值一样 , 两个变都可以修改值 , 怎么修改它们的结果都是一模一模样的 .
浅拷贝 : id值不一样 , 存的值id一样 , 修改不可变类型不影响另一个变量 , 修改可变类型影响另一个变量 .
深拷贝 : id值不一样 , 存的值一样 , 怎么修改都不影响另一个变量 .
3.练习
1. 分别画出下面两个列表在内存中是如何存放的
l1 = [ 11 , 22 , [ 333 , 444 ] ]
l2 = [ 11 , 22 , [ 33 , { 'name' : 'kid' , 'age' : 18 } ] ]
2. 元组的元素为一个可变类型时 , 修改这个容器中元素值的图
3. 深浅拷贝变量指向数据的图