Python变量的赋值、浅拷贝和深拷贝详解

 更新时间:2023年11月04日 10:04:09   作者:惊瑟  
这篇文章主要介绍了Python变量的赋值、浅拷贝和深拷贝详解,python中为声明一个变量有三种方法:赋值、浅拷贝、深拷贝,相信每个pythoner或多或少都知道他们之间的区别,但在某些点上,还是会踩坑,这篇文章记录下所有关于这三者区别的疑问,需要的朋友可以参考下

前言

python中为声明一个变量有三种方法:赋值、浅拷贝(shadow copy)、深拷贝(deep copy),相信每个pythoner或多或少都知道他们之间的区别,但在某些点上,还是会踩坑,这篇文章记录下所有关于这三者区别的疑问。

对比

在理解以上几个概念区别之前需要明确以下问题:

  • 赋值传递的是原始对象的地址
  • 函数传递形参也是传递地址,该地址指向原始对象,多进程的task函数除外,多进程中task函数的形参和原始对象是完全不同的两个对象,类似深拷贝(进程间内存独立)
  • 浅拷贝,除了显式地调用copy库,还可以使用如切片、list()、dict()等函数达到浅拷贝的效果(下面会给例子)。
  • 当我们讨论python中的拷贝时,默认针对的是可变对象,因为对于不可变对象来讲,拷贝是没有意义的,python解释器在启动时就将不可变对象放在内存池中了,它在内存的位置在解释器终止之前不会变。假设我们通过赋值、浅拷贝、深拷贝得到了一个新的对象,那么与原始对象相比,有以下结论:
/赋值浅拷贝深拷贝
id是否变化×
修改对象本身是否互相影响××
修改嵌套对象是否互相影响×

具体例子

a = ['a', 1, [1,2]]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)

print(f'a is: {a} , id is: {id(a)}')
print(f'b is: {b} , id is: {id(b)}')
print(f'c is: {c} , id is: {id(c)}')
print(f'd is: {d} , id is: {id(d)}')

输出:

a is: ['a', 1, [1, 2]] , id is: 140211144620672
b is: ['a', 1, [1, 2]] , id is: 140211144620672
c is: ['a', 1, [1, 2]] , id is: 140211144622016
d is: ['a', 1, [1, 2]] , id is: 140211144620992

可以看出,赋值得到的新对象id与原始对象id相同,拷贝得到的新对象id与原始对象不同。

# 对对象本身进行修改,即“最外层”
a.append(2)

print(f'a is: {a} , id is: {id(a)}')
print(f'b is: {b} , id is: {id(b)}')
print(f'c is: {c} , id is: {id(c)}')
print(f'd is: {d} , id is: {id(d)}')

输出:

a is: ['a', 1, [1, 2], 2] , id is: 140211144620672
b is: ['a', 1, [1, 2], 2] , id is: 140211144620672
c is: ['a', 1, [1, 2]] , id is: 140211144622016
d is: ['a', 1, [1, 2]] , id is: 140211144620992

可以看出,修改对象本身对拷贝对象没有影响。 再修改嵌套对象:

# 修改嵌套对象
a[2].append(3)

print(f'a is: {a} , id is: {id(a)}')
print(f'b is: {b} , id is: {id(b)}')
print(f'c is: {c} , id is: {id(c)}')
print(f'd is: {d} , id is: {id(d)}')

输出:

a is: ['a', 1, [1, 2, 3], 2] , id is: 140211144620672
b is: ['a', 1, [1, 2, 3], 2] , id is: 140211144620672
c is: ['a', 1, [1, 2, 3]] , id is: 140211144622016
d is: ['a', 1, [1, 2]] , id is: 140211144620992

可以看出,修改嵌套对象,对浅拷贝对象有影响,对深拷贝对象无影响。 再看下不可变对象1的id:

print(f'1 id in a is {id(a[1])}')
print(f'1 id in b is {id(b[1])}')
print(f'1 id in c is {id(c[1])}')
print(f'1 id in d is {id(d[1])}')

输出:

1 id in a is 140279957373168
1 id in b is 140279957373168
1 id in c is 140279957373168
1 id in d is 140279957373168 

可以看出,所有对象的不可变对象指向了同一个位置,事实上,即便在同一个对象中相同的不可变对象也是指向同一个位置:

print(f'a is {a}')
print(f'{id(a[1])}, {id(a[2][0])}')

输出:

a is ['a', 1, [1, 2, 3], 2]
140168057503984, 140168057503984

除了显示地使用copy.copy()获得一个浅拷贝对象,list()、dict()等对象工厂也可以获得一个浅拷贝对象:

a = ['a', 1, [1,2]]
b = list(a)
print(f'a is: {a} , id is: {id(a)}')
print(f'b is: {b} , id is: {id(b)}')

a[2].append(3)
print(f'a is: {a} , id is: {id(a)}')
print(f'b is: {b} , id is: {id(b)}')

输出:

a is: ['a', 1, [1, 2]] , id is: 139717765197952
b is: ['a', 1, [1, 2]] , id is: 139718287865280
a is: ['a', 1, [1, 2, 3]] , id is: 139717765197952
b is: ['a', 1, [1, 2, 3]] , id is: 139718287865280

可以看出,a与b的id不同,但修改嵌套对象会互相影响,所以b是a的浅拷贝对象。

切片也类似,是一个浅拷贝对象:

a = ['a', 1, [1,2]]
b = a[-1:]
print(f'a is: {a} , id is: {id(a)}')
print(f'b is: {b} , id is: {id(b)}')

a[2].append(3)
print(f'a is: {a} , id is: {id(a)}')
print(f'b is: {b} , id is: {id(b)}')

输出:

a is: ['a', 1, [1, 2]] , id is: 140294636448256
b is: [[1, 2]] , id is: 140294636449408
a is: ['a', 1, [1, 2, 3]] , id is: 140294636448256
b is: [[1, 2, 3]] , id is: 140294636449408

到此这篇关于Python变量的赋值、浅拷贝和深拷贝详解的文章就介绍到这了,更多相关Python赋值、浅拷贝和深拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python实现cpu并行运算的两种方式

    Python实现cpu并行运算的两种方式

    Python 提供了多种库来支持 CPU 并行运算,其中最常用的是 multiprocessing 和 mpi4py 模块,这两个库允许你在单台机器上利用多核处理器提高程序的性能,本文通过代码示例给大家详细的介绍了这两种方法,需要的朋友可以参考下
    2024-08-08
  • Python3.5基础之变量、数据结构、条件和循环语句、break与continue语句实例详解

    Python3.5基础之变量、数据结构、条件和循环语句、break与continue语句实例详解

    这篇文章主要介绍了Python3.5基础之变量、数据结构、条件和循环语句、break与continue语句,结合实例形式详细分析Python3.5编程入门相关的变量、数据结构、常用条件与循环语句操作技巧及注意事项,需要的朋友可以参考下
    2019-04-04
  • 利用Numba与Cython结合提升python运行效率详解

    利用Numba与Cython结合提升python运行效率详解

    近些年来, Numba和Cython在数学科学界得到了广泛的关注。它们都提供了一种加速CPU密集型任务的方法,但以不同的方式。本文描述了它们之间体系结构的差异
    2021-09-09
  • python利用拉链法实现字典方法示例

    python利用拉链法实现字典方法示例

    这篇文章主要介绍了python利用拉链法实现字典的方法,文中给出了详细的示例代码,相信对大家具有一定的参考价值,需要的朋友可以们下面来一起看看吧。
    2017-03-03
  • 基于python计算滚动方差(标准差)talib和pd.rolling函数差异详解

    基于python计算滚动方差(标准差)talib和pd.rolling函数差异详解

    这篇文章主要介绍了基于python计算滚动方差(标准差)talib和pd.rolling函数差异详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-06-06
  • Python多线程中线程数量如何控制

    Python多线程中线程数量如何控制

    本文主要介绍了Python多线程中线程数量如何控制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • 利用python-pypcap抓取带VLAN标签的数据包方法

    利用python-pypcap抓取带VLAN标签的数据包方法

    今天小编就为大家分享一篇利用python-pypcap抓取带VLAN标签的数据包方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • Python+tkinter实现网站下载工具

    Python+tkinter实现网站下载工具

    这篇文章主要为大家详细介绍了如何利用Python+tkinter实现网站下载工具,实现所有数据一键获取,文中的示例代码讲解详细,感兴趣的可以了解一下
    2023-03-03
  • Python时间差中seconds和total_seconds的区别详解

    Python时间差中seconds和total_seconds的区别详解

    今天小编就为大家分享一篇Python时间差中seconds和total_seconds的区别详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • Python 实现驱动AI机器人

    Python 实现驱动AI机器人

    这篇文章主要介绍了Python 实现驱动AI机器人,下文围绕利用Python 实现驱动AI机器人的相关资料展开内容,需要的小伙伴可以参考一下
    2022-02-02

最新评论