python的深浅拷贝原理 python值传递和引用传递

一、变量存储

变量的存储分为栈区和堆区,两者之间是引用关系,变量名放在栈区内存,真实的数据存放在堆内存里

栈区通过指针来指向对应的堆区内存

 

 二、直接赋值

链式赋值是直接引用栈区变量名,相当于一个人起两个名字,表示的还是一毛一样的一个对象

l = [1,2,[1,2]]
l1 = l
print(id(l),id(l1)) #43418120  43418120 两个列表一样

 

三、浅拷贝

浅拷贝只复制指向堆区的指针,而不复制对象本身,新旧对象还是共享同一块内存

对于不可变类型指向的是真实数据,对于可变类型指向的是该对象的栈区

 

改变真身内不可变类型,本质是真身重新申请内存改变自己的指针,不改变原内存,不影响拷贝的指针指向,拷贝的相应元素保持不变

改变真身内可变类型,拷贝的相应元素跟着变化

 

 

import copy
l = [1,2,[1,2]]

l1 = copy.copy(l)  # 拷贝一份 
print(id(l),id(l1)) #656 936 浅拷贝来的不是原来的对象,而是创建了一个新对象
print(id(l[0]),id(l[1]),id(l[2]),)    #224 256 928 
print(id(l1[0]),id(l1[1]),id(l1[2]),) #224 256 928 浅拷贝和原来指向的是同一块内存

l[0] = 222
print(l,l1)  #[222, 2, [1, 2]]  [1, 2, [1, 2]]
print(id(l[0]),id(l[1]),id(l[2]),)    #296 256 928 改变不可变类型的值是重新在堆区申请了一块内存,id改变
print(id(l1[0]),id(l1[1]),id(l1[2]),) #224 256 928 

l[2].append(666)
print(l,l1)  #[222, 2, [1, 2, 666]]  [1, 2, [1, 2, 666]]
print(id(l[0]),id(l[1]),id(l[2]),)     #296 256 928 改变可变类型其id不变(可变类型是堆区里面又套栈区,改变对于列表本身类似于链式赋值,起别名,你细品)
print(id(l1[0]),id(l1[1]),id(l1[2]),)  #224 256 928 

四、深拷贝

不管是可变还是不可变类型,统统指向真实的数据,所以不管真身怎么变,拷贝来的都不变

拷贝出来id是不一样的,但是有个别时候会违背常理出现id一样,这是小整数池造成的

 

 

import copy
l = [1,2,[1,2]]
l1 = copy.deepcopy(l)  # 拷贝一份

print(id(l),id(l1)) #656 936 深拷贝来的也不是原来的对象,而是创建了一个新对象
print(id(l[0]),id(l[1]),id(l[2]),id(l[2][0]),id(l[2][1]),)      #224 256 296 224 256 
print(id(l1[0]),id(l1[1]),id(l1[2]),id(l1[2][0]),id(l1[2][1]),) #224 256 632 224 256

 五、值传递和引用传递

python基本的参数传递机制有两种:值传递和引用传递

  值传递(passl-by-value)过程中,被调函数的形式参数作为被调函数的局部变量处理,即在堆栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。

  引用传递(pass-by-reference)过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量

不可变参数是值类型,值传递:数字,字符串,元组

可变参数,引用传递,传过去后可以被修改内存,如:列表,字典

 

 

 

 

ps:对于不可变类型尤其注意坑点:

task = {'test':'就TM离谱'}
waiting_recog = {}
recoging_tasks = {}

task['num_recoging'] = 0
for i in range(2):
    num = i
    waiting_recog[num] = task
    task['num_recoging'] += 1
recoging_tasks['task_id'] = task

print(waiting_recog)
print(recoging_tasks)
"""
{0: {'test': '就TM离谱', 'num_recoging': 2}, 1: {'test': '就TM离谱', 'num_recoging': 2}}
{'task_id': {'test': '就TM离谱', 'num_recoging': 2}}
"""

 ### 实际应用举例

 

 

posted @ 2020-04-02 23:58  www.pu  Views(737)  Comments(0Edit  收藏  举报