python 深拷贝与浅拷贝

本文深入探讨Python中对象的概念,包括不可变与可变对象的特点,以及赋值、浅拷贝与深拷贝的区别。通过具体示例展示了不同类型的对象在内存中的行为表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Python 变量

在Python中,变量都是指针,指针的内存空间与数据类型无关,其内存空间保存了指向数据的内存地址。
在C中,当定义一个变量后,编译器就一会给该变量分配内存,后续对该内存的读写是通过该内存实现的,而在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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值