python对象的引用_Python基础-17对象引用和拷贝

17.对象引用和拷贝

我们先来看看以下向个概念

变量:是系统变量名表中的元素,通常是由程序员进行定义声明

对象:是计算机分配的一块内存,需要足够的空间去表示它的值

引用:是自动形成的从变量到对象的指针

可变对象:允许对自身内容进行修改。如list、dict、set、自定义类型等。

不可变对象:不允许对自身内容进行修改。如果对一个不可变对象进行赋值,实际上是生成一个新的对象,再让变量指向这个对象。如int、float、bool、str、tuple

如果有了解Java的堆栈知识(堆存储真实的数据,而栈则是存储相应引用地址),则这里所指的对象可以理解为堆,而引用则代表栈。

17.1 对象引用

对象的赋值实际上就是对象引用,创建一个对象并将其赋值给一个变量时,该变量实际是指向了该对象的引用,可使用内置函数id()查看返回值。变量名与对象之间的示意图如下所示:

示例如下所示:

>>> tempA=[1,3,5]

>>> tempB=tempA # tempB对tempA的引用

>>> tempB

[1, 3, 5]

>>> tempB[0]=-100 # 修改tempB的元素,tempA相应的元素也同步进行了更改

>>> tempA

[-100, 3, 5]

>>> tempB

[-100, 3, 5]

>>> id(tempA),id(tempB)

(2614814009544, 2614814009544)

>>> tempB is tempA

True

在上面的例子中,本意是想修改tempB中第一个元素,而连带temA也被一起修改了。因为tempA和tempB引用的是同一个对象,修改其中任意一个变量都会影响到另一个。为了避免这种情况,必须创建对象的副本而不是创建新引用。对于像列表和字典这种容器类对象,可以使用两种拷贝操作:浅拷贝和深拷贝。

17.2 对象的拷贝

17.2.1 浅拷贝

浅拷贝将创建一个新对象,其内容是原对象中元素的引用。可以使用模块copy中的copy()函数,另外也可使用切片操作、对象的copy方法。其特点如下所示:

两个变量的内存地址不同

变量之间存在共享值的情况

对其中一个变量进行更改后,另外的变量也会随之改变

如果使用等号赋值时,连对象都不会重新创建。只有重新创建对象并为其赋值,才会发生浅拷贝。

示例代码如下所示:

>>> a=[1,2,[3,4]]

>>> b=list(a) # 创建a的一个浅复制

>>> b is a

False

>>> b.append(100) # 给b追加一个元素

>>> b

[1, 2, [3, 4], 100] # 修改b中的一个元素

>>> a

[1, 2, [3, 4]]

>>> b[2][0]=-98

>>> b

[1, 2, [-98, 4], 100]

>>> a # a中与b共有的元素值也会发生改变

[1, 2, [-98, 4]]

>>> id(a),id(b)

(2614813897288, 2614813796232)

>>> aa=[1,2,[3,4]]

>>> bb=aa # 直接赋值并没有发生浅拷贝

>>> id(aa),id(bb)

(1960262980168, 1960262980168)

>>> aa = list(bb)

>>> id(aa),id(bb)

(1960263019208, 1960262980168) # 发生了浅拷贝,因此两者的id也不一样

>>> id(aa[0]),id(aa[1]),id(aa[2])

(140715523797264, 140715523797296, 1960263020232)

>>> id(bb[0]),id(bb[1]),id(bb[2])

(140715523797264, 140715523797296, 1960263020232) # 虽然发生了浅拷贝,但内部元素却都指向相同的对象

在上述示例中,a和b是单独的列表对象,但它们包含的元素是共享的。因此修改b的一个元素也会修改a中的对应元素。而在aa和bb中,在发生浅拷贝后,aa和bb两个对象的地址不一样,而其内部元素却指向了相同的对象。

17.2.2 深拷贝

深拷贝将创建一个新对象并对其赋值时,原对象中的所有元素都会在新对象中重新创建一次。常用模块copy中的deepcopy()函数,其特点如下所示:

变量间的内存地址不同

变量间有各自的值,且互不影响

对其任意一个变量的值进行修改,不会影响另外一个

示例代码如下所示:

>>> import copy

>>> a=[1,2,[3,4]]

>>> b=copy.deepcopy(a) # 深拷贝

>>> b is a

False

>>> b.append(100)

>>> b

[1, 2, [3, 4], 100]

>>> a

[1, 2, [3, 4]]

>>> b[2][0]=-98

>>> b

[1, 2, [-98, 4], 100]

>>> a # 在修改b之后,对a没有任何影响

[1, 2, [3, 4]]

>>> id(a),id(b)

(1960263017096, 1960263112136)

>>> aa=[1,2,[3,4]]

>>> bb=copy.deepcopy(aa) # 深拷贝

>>> id(aa),id(bb)

(2540655565384, 2540656480520) # 地址发生改变

>>> id(aa[0]),id(aa[1]),id(aa[2])

(140715523797264, 140715523797296, 2540655563912)

>>> id(bb[0]),id(bb[1]),id(bb[2])

(140715523797264, 140715523797296, 2540656401224)

在打印内部地址发现,前两个元素地址没有属性改变,是因为在Python数字和字符串属于不可变对象。为提升效率,Python语言中,在内存中只存在一份不可变对象,并将其地址(即引用)赋值给其他变量。

浅拷贝和深拷贝仅仅是针对可变对象的,对于不可变对象,赋值的操作过程都是直接将引用赋值。

17.3 小结

现在假设有一个对象a=[ 1, 2 ,[ 3,4 ] ],有另外一个对象,分别进行=赋值、浅拷贝和深拷贝,其使用小结如下所示:

1.使用=直接赋值,不会发生浅拷贝和深拷贝情况,仅相当于增加一个新标签,并不产生新的对象,示意图如下所示:

针对这种情况,有时候也被比喻为旧瓶装旧酒。

2.使用浅拷贝之后,会创建一个新的对象,但内部元素仍然保持一致,示意图如下所示:

因为元素中1和2为不可变对象,它们互不影响,给人的感觉就相当于复制了一份。这种就是浅拷贝,有时候也被比喻为新瓶装旧酒,虽然产生了新的对象,但里面的内容还是来自同一份。

3.使用深拷贝之后,会创建一个新的对象,原对象中的所有元素会被重新创建一次,示意图如下所示:

对象a和b前两个元素因是不可变对象,所会在进行深拷贝之后,地址不会进行更改。而第三个元素为可变对象,则相当创建了一个副本。所以深拷贝也可以理解为,不仅是对象自身的拷贝,对于对象中每一个子元素,也都进行同样的拷贝。针对这种情况,有时候也被比喻为新瓶装新酒

4.浅拷贝和深拷贝针对的是可变对象

本文同步在微信订阅号上发布,如各位小伙伴们喜欢我的文章,也可以关注我的微信订阅号:woaitest,或扫描下面的二维码添加关注:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值