变量的实现方式
-
Python中的所有变量都是指针(引用),变量里存的是值的地址,因此所有变量占用的空间都一样(地址的大小)。这种存储方式被称为指针语义(引用语义、对象语义)。通过
id(变量)
查看变量所指向的地址 -
C语言中,不同类型变量占用空间不同(int占4字节,char占1字节),可以看出:变量里存的是值本身。这种方式称为值语义
不可变&可变数据类型
不可变数据类型 Immutable
int
,float
,str
,tuple
不允许内存中的值发生变化,要修改值(运算)的情况,不会将修改后的值放回(覆盖)原地址,而是直接新增一块地址空间,来存放修改后的值,然后使变量(引用)指向新的地址空间
除tuple外,不可变数据在整个内存中是唯一的,相同值只能存在一个地址中
可变数据类型 Mutable
list
,dict
,class
我理解成容器类型(二级指针?指向数组的指针?),允许容器内的值发生变化(append
或+=
),而容器本身的地址不会变化,容器内的元素也可以是容器
在整个内存中,相同值可以存在多份(多个不同地址空间),由此想到以下两种情况:
-
相同的容器(立即数?)赋值给多个变量(不是赋值拷贝):这些变量的
id
不一样 -
多次赋值给同一变量(不是赋值拷贝):变量的
id
会发生变化(那上一个地址空间被销毁了吗?)
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a)
140508626205888
>>> id(b)
140508626146176
>>> a = [1, 2, 3] # 上一个地址(...5888)就被回收了,下次赋值时使用
>>> id(a)
140508626145984
>>> c = [1, 2, 3] # 使用了上面被回收的地址(...5888)
>>> id(c)
140508626205888
'''
感觉tuple像介于可变和不可变之间的东西
即满足容器的一些特性
又不能修改容器的内容
'''
>>> a = (1, 2, 3)
>>> b = (1, 2, 3)
>>> id(a)
140274137290688
>>> id(b)
140274137163712
>>> a += (4, 5)
>>> id(a)
140274137546560
底层实现的猜想:
- 每当给变量赋值一个容器(立即数?)时,都会在一个新的地址空间中生成容器,然后让变量指向(引用)该容器
- 如果赋给同一变量,那么之前的地址空间就被回收,供下次使用
浅拷贝和深拷贝
3种拷贝方式:ref
赋值拷贝(=)
只是新建一个引用,两个变量(引用)指向同一个地址
如果把引用比作快捷方式,那么=
就相当于新增了一个快捷方式,指向相同的地址
函数传参相当于形参=实参
浅拷贝(copy)
只拷贝父对象,不拷贝父对象内部的子对象,只拷贝子对象的引用
只拷贝第一层变量(最外层容器本身),内部变量相当于赋值拷贝(快捷方式)
深拷贝(deepcopy)
完全拷贝所有内容,开辟全新的内存空间
import copy
a = [1, [2, 3]]
a_ = a
a_copy = a.copy() # <==> a_copy = copy.copy(a)
a_deepcopy = copy.deepcopy(a)
print(id(a)) # 139751405433920
print(id(a[0])) # 93898578046432
print(id(a[1])) # 139751404912448
print(id(a_)) # 139751405433920
print(id(a_[0])) # 93898578046432
print(id(a_[1])) # 139751404912448
print(id(a_copy)) # 139751406233856
print(id(a_copy[0])) # 93898578046432
print(id(a_copy[1])) # 139751404912448
print(id(a_deepcopy)) # 139751405539072
print(id(a_deepcopy[0])) # 93898578046432
print(id(a_deepcopy[1])) # 139751406590080
Hash
有__hash__()
方法的对象称为Hashable
不可变对象一定是可哈希的Hashable,Hashable不一定是不可变对象
hash(object)
返回object.__hash__()
方法的值(根据机器字长进行截断)
class A:
def __hash__(self):
return 1
a = A()
hash(a) # 1
What does “hashable” mean in Python?
Python中的__hash__和__eq__方法之间的一些使用问题
总结
- Python中所有变量都是引用(指针)
- 不可变数据发生变化时,生成新值,而不是修改原值,相同的值在内存中是唯一的
- 可变数据变化时,不会重新分配新的容器地址,只改变容器内的变量
- 三种拷贝方式:对于不可变数据,不管哪种拷贝,都只是快捷方式,
id
都相同。只有操作可变数据,才能体现出差异性