Python 变量
在Python中,变量都是指针,指针的内存空间与数据类型无关,其内存空间保存了指向数据的内存地址。
Python 对象
在Python 中,一切皆对象,主要由以下部分组成:
(1)identity(ID):标识对象的“内存地址”,可使用id(obj)获取(唯一标识)
(2)type(类型):标识对象的“类型”,可使用type(obj)获取
(3)value(值):标识对象的“值”,可使用print(obj)获取
注:对象的本质是一个内存块
注:变量无类型,对象有类型;变量位于栈内存,对象位于堆内存。
在python中,对象可分为 不可变对象 和 可变对象,如下图所示:
不可变对象
不可变对象包括:bool,int,float,str,tuple,frozenset,具有以下特性:
可hash(不可变长度)
不支持新增
不支持删除
不支持修改
支持查询
可变对象
可变对象包括:list,set,dict,具有以下特性:
不可hash(可变长度)
支持新增
支持删除
支持修改
支持查询
什么是赋值引用
拷贝对象的引用(即指针),使得两个变量指向同一个对象
>>> a = {1: [1, 2, 3]}
>>> b = a
>>> print(a is b)
True
>>> print(id(a))
4468940880
>>> print(id(b))
4468940880
什么是浅拷贝(copy)
仅拷贝父对象,但不拷贝父对象中的子对象。
注:子对象共享内存,指向同一个对象。
>>> a = {1: [1, 2, 3]}
>>> b = a
>>> print(a is b)
True
>>> print(id(a))
4468940880
>>> print(id(b))
4468940880
什么是深拷贝(deepcopy)
拷贝父对象,同时传递拷贝所有子对象
注:如果子对象为不可变对象,则共享内存;如果子对象为可变对象,则不共享内存。
>>> a = {1: [1, 2, 3]}
>>> import copy
>>> b = copy.deepcopy(a)
>>> print(a is b)
False
>>> print(id(a))
140394609531776
>>> print(id(b))
140394611327680
Python 参数传递 是值传递还是引用传递
在Python参数传递中,不存在所谓值传递和引用传递的区分,其本质都是拷贝对象的引用(指针)传递。
>>> def fun(a):
... return a
>>> b = 1
>>> print(fun(b))
1
上述代码所示,在执行fun(b)代码是,其等价执行的是fun(a=b),其本质是一种赋值引用;所以 ## 在Python中,对象只存在引用转递。##
不可变对象:外部永远不随内部变# 不可变对象
>>> def fun(b):
... b = -1
... return b
>>> a = 1
>>> print(fun(a))
-1
>>> print(a)
1
可变对象:外部跟随内部变
可变对象
>>> def fun(b):
... b.append(-1) # 修改原对象
... return b
>>> a = [1]
>>> print(fun(a))
[1, -1]
>>> print(a)
[1, -1]
可变对象:外部不随内部变
>>> def fun(b):
... b = [-1] # 产生新对象
... return b
>>> a = [1]
>>>> print(fun(a))
>[-1]
>>>> print(a)
>[1]
可变对象举例:
设置两个变量i,j;
输出变量i,j在内存中的id,结果显示其都指向同一块内存,所以说i和j都是指向同一个对象;
然后修改j的值,让j的值+1,按道理j修改之后,i的值应该也会发生改变,因为它们都是指向的同一块内存,但结果显示并没有。原因在于int类型是不可变对象,所有在内存中,j被复制到一份新的内存地址中,然后+1,所以j的内存id发生了变化。
if __name__ == '__main__':
i = 123
j = 123
print("i=%d,i的内存地址为:%s"%(i,id(i)))
print("j=%d,j的内存地址为:%s"%(j,id(j)))
j += 1
print("j=%d,j的内存地址为:%s"%(j,id(j)))
'''
i=123,i的内存地址为:140730843266624
j=123,j的内存地址为:140730843266624
j=124,j的内存地址为:140730843266656
'''
不可变对象举例:
i最早的内存地址id是1945693038856;
然后把i赋值给j,其实就是让变量j的也指向i所指向的内存空间;
然后我们发现当i发生变化后,j也跟着发生变化了,因为list是可变类型,所以并不会复制一份再改变,而是直接在i所指向的内存空间修改数据,而j也是指向该内存空间的,自然也就跟着改变了。
if __name__ == '__main__':
i = [1,2,3]
j = i
print("i=%s,i的内存地址为:%s"%(i,id(i)))
print("j=%s,j的内存地址为:%s"%(j,id(j)))
i.append(4)
print("i=%s,i的内存地址为:%s" % (i, id(i)))
print("j=%s,j的内存地址为:%s"%(j,id(j)))
'''
i=[1, 2, 3],i的内存地址为:1945693038856
j=[1, 2, 3],j的内存地址为:1945693038856
i=[1, 2, 3, 4],i的内存地址为:1945693038856
j=[1, 2, 3, 4],j的内存地址为:1945693038856
'''
浅拷贝和深拷贝
不管操作是浅拷贝还是深拷贝,拷贝出来的新对象的地址和原对象是不一样的。
但是新对象里面的可变对象(如列表)的地址和原对象里的可变元素的地址是相同的,也就是说浅拷贝中拷贝的是浅层次的数据结构(不可变对象),对象里的可变元素作为深层次的数据结构并没有被拷贝到新地址里面去,而是和原对象里的可变元素指向同一个地址,所以在新对象或原对象里对这个可变元素做修改时,两个对象是同时改变的,但是深拷贝不会这样,这个是浅拷贝相对于深拷贝最根本的区别。
浅拷贝
import copy
if __name__ == '__main__':
old_list = [1, [1, 2, 3], 3]
new_list = copy.copy(old_list)
print('Before:')
print(old_list)
print(id(old_list))
print(new_list)
print(id(new_list))
new_list[0] = 3
new_list[1][0] = 3
print('After:')
print(old_list)
print(id(old_list))
print(new_list)
print(id(new_list))
'''
Before:
[1, [1, 2, 3], 3]
1634384110984
[1, [1, 2, 3], 3]
1634384275080
After:
[1, [3, 2, 3], 3]
1634384110984
[3, [3, 2, 3], 3]
1634384275080
'''
深拷贝
import copy
if __name__ == '__main__':
old_list = [1, [1, 2, 3], 3]
new_list = copy.deepcopy(old_list)
print('Before:')
print(old_list)
print(id(old_list))
print(new_list)
print(id(new_list))
new_list[0] = 3
new_list[1][0] = 3
print('After:')
print(old_list)
print(id(old_list))
print(new_list)
print(id(new_list))
'''
Before:
[1, [1, 2, 3], 3]
2192491128328
[1, [1, 2, 3], 3]
2192491210696
After:
[1, [1, 2, 3], 3]
2192491128328
[3, [3, 2, 3], 3]
2192491210696
'''
参考文章:
https://blog.youkuaiyun.com/weixin_42009804/article/details/121078734
https://blog.youkuaiyun.com/tailonh/article/details/113664643