python中变量的赋值、浅拷贝、深拷贝

本文详细介绍了Python中深拷贝和浅拷贝的概念及区别,通过实例展示了如何在可变对象如DataFrame中正确创建副本以避免意外修改。文章解释了不可变对象与可变对象在赋值、浅拷贝和深拷贝操作下的行为差异,并提供了代码示例以加深理解。最后,作者给出了如何解决因引用导致的意外改变问题的方法,强调在处理可变对象时使用深拷贝以确保副本的独立性。
摘要由CSDN通过智能技术生成
问题

写一段测试程序,我本意是想从一个DataFrame对象复制出来一个独立的副本出来,代码如下:

df = pd.DataFrame()

df1 = df

# 改变df1的元素
df1[row][col] = 'b'

print(df)
print(df1)

结果打印出来的df也改变了。

也就是说,在改变df1的时候df也改变了,它们实际指向的同一块内存。

如何解决这个问题呢,查阅了一些资料,把原理和解决思路作一记录。

两类对象

首先来了解一下python中的两类对象:不可变对象和可变对象。

  • 不可变对象:创建后就不可修改的对象,如数值、字符串和无组等
  • 可变对象:创建后仍可修改,如列表、字典、集合、DataFrame等

这样说有点不明所以,它的内存意思是:

  • 不可变对象:该对象所指向的内存中的值不能被改变。当修改变量时,相当于把原来的值拷贝一次再修改,这会开辟出一块新内存,变量也指向这个新内存地址
  • 可变对象:该对象所指向的内存中的值可以被改变。当修改变量时,直接在内存上修改,所有指向这个内存地址的变量都会发生变化

知道了原理,也就可以深入理解一下赋值和拷贝了。

赋值和拷贝

赋值比较常用,也好理解,使用=操作。

拷贝分为浅拷贝和深拷贝,下面分别介绍。

  • 赋值:只是复制了新对象的引用,不会开辟新内存。如 a = 1,实际上先创建变量a,再分配内存存储值1,最后将变量a与该内存地址使用引用对应起来。于是每次取a时,得到的是1
  • 浅拷贝:创建一个新对象,内容是原对象的引用。有三种实现方式:切片、工厂和copy函数。注意它只拷贝了最外围对象一层,如果对象内嵌套有对象,那么对于嵌套对象,只得到引用而不会得到副本
  • 深拷贝:相对于浅拷贝而言,它拷贝出来的是完全独立的一个副本,即使对象有多层嵌套,也会完全拷贝出来一个全新对象。实现方式是copy模块中的deepcopy函数

从以上分析可以看出,对于不可变对象,赋值、浅拷贝和深拷贝的结果是一样的,如下代码:

a = 10
b = a
b = 20
print('赋值:', a, b)
# 赋值: 10 20

import copy
a = 10
b = a
b = copy.copy(a)
b = 20
print('copy:', a, b)
# copy: 10 20

a = 10
b = a
b = copy.deepcopy(a)
b = 20
print('deepcopy:', a, b)
# deepcopy: 10 20

对可变对象,赋值与拷贝的结果就不同了。

由于浅拷贝对对象是否嵌套结果会有不同,分别示例测试一下。

对于没有嵌套的简单对象,copy和deepcopy的结果一致,都得到了对象的独立副本,副本的改变不会改变原值:

a = [1,2,3]
b = a
b[0] = 8
print('赋值:', a, b)
# 赋值: [8, 2, 3] [8, 2, 3]

import copy
a = [1,2,3]
b = copy.copy(a)
b[0] = 8
print('copy:', a, b)
# copy: [1, 2, 3] [8, 2, 3]

a = [1,2,3]
b = copy.deepcopy(a)
b[0] = 8
print('deepcopy:', a, b)
# deepcopy: [1, 2, 3] [8, 2, 3]

对于有嵌套对象的对象,当修改对象外层元素时,浅拷贝和深拷贝结果一样,而修改嵌套内元素时,只有深拷贝副本不会改变原值:

a = [1,2,3,[1,2]]
b = a
b[3][0] = 8
print('赋值:', a, b)
# 赋值: [1, 2, 3, [8, 2]] [1, 2, 3, [8, 2]]

import copy
a = [1,2,3,[1,2]]
b = copy.copy(a)
b[3][0] = 8
print('copy:', a, b)
# copy: [1, 2, 3, [8, 2]] [1, 2, 3, [8, 2]]

a = [1,2,3,[1,2]]
b = copy.deepcopy(a)
b[3][0] = 8
print('deepcopy:', a, b)
# deepcopy: [1, 2, 3, [1, 2]] [1, 2, 3, [8, 2]]
小结

明白了以上示例后,开篇的问题自然不在话下。

使用赋值对可变对象进行操作,它们引用的仍是同一块内存,且变量的改变是直接改变内存值。

一个变量的改变自然也会影响到另一变量。

使用深拷贝解决即可。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值