python:复制

默认做浅复制

复制序列最简单的方法是使用内置的类型构造方法。比如:

l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = list(l1)  #创建l1的副本
print(l2 == l1 )   # True:  相等
print( l2 is l1 )  # False: 但是不相同

l1.append(100)  #把 100 追加到 l1 中,对 l2 没有影响。

print(l1)  #[3, [66, 55, 44], (7, 8, 9), 100]
print(l2)  #[3, [66, 55, 44], (7, 8, 9)]


l1[1].remove(55) #把内部列表 l1[1] 中的 55 删除。这对 l2 有影响,因为 l2[1] 绑定的列表与 l1[1] 是同一个。
print(l1)  #[3, [66, 44], (7, 8, 9), 100]
print(l2)  #[3, [66, 44], (7, 8, 9)]


l2[1] += [33, 22] #对可变的对象来说,+= 运算符就地修改列表
print(l1)  #[3, [66, 44, 33, 22], (7, 8, 9), 100]
print(l2)  #[3, [66, 44, 33, 22], (7, 8, 9)]

l2[2] += (10, 11) #对元组来说,+= 运算符创建一个新元组
print(l1)  #[3, [66, 44, 33, 22], (7, 8, 9), 100]
print(l2)  #[3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]

在这里插入图片描述
然而,构造方法或者[:]做的是浅复制(即复制了最外层容器,副本中的元素是源容器中元素的引用)。

  • 如果所有元素都是不可变的,那么可以节省内存
  • 如果有可变的元素,那么就可能导致问题

浅复制容易操作,但是得到的结果可能并不是你想要的

为任意对象做深复制和浅复制

copy模块提供的 deepcopy 和 copy 函数能为任意对象做深复制和浅复制。

class Point :
    """ Represents ␣a␣ point ␣in␣2−D␣ space ."""


class Rectangle :
    """ Represents ␣a␣ rectangle .
    ␣␣␣␣ attributes :␣width ,␣ height ,␣ corner .
    ␣␣␣␣ """

别名会降低程序的可读性,因为一个地方的变动可能对另一个地方造成预料之外的影响。跟踪所有引用同一个对象的变量是非常困难的。

通常用复制对象的方法取代为对象起别名。copy 模块拥有一个叫做 copy 的函数,可以复制任何对象:

p1 = Point()
p1.x = 3.0
p1.y = 4.0


import copy
# p1 和 p2 拥有相同的数据,但是它们并不是同一个 Point 对象。
p2 = copy.copy(p1)
p3 = p1

print(p1) # <__main__.Point object at 0x000002321E3BBD30>
print(p2) #<__main__.Point object at 0x0000029DEC3FAE30>
print(p3) # <__main__.Point object at 0x0000029DEC3FBD30>
print(p1 is p2) #False
print(p1 == p2) #False
print(p1 is p3) #True
print(p1 == p3) #True

==运算符的默认行为和 is 运算符相同;它检查对象的标识 (identity) 是否相同,而非对象的值是否相同。

如果你使用 copy.copy 来复制一个 Rectangle ,你会发现它仅仅复制了 Rectangle 对象,但没有复制嵌套的 Point 对象。

在这里插入图片描述


box = Rectangle ()
box.width = 100.0
box.height = 200.0
box.corner = Point ()
box.corner.x = 0.0
box.corner.y = 0.0


box2 = copy.copy(box)

print(box) # <__main__.Rectangle object at 0x0000015F1C97BD60>
print(box2) #<__main__.Rectangle object at 0x0000015F1C97B2B0>
print(box is box2) #False
print(box == box2) #False
print(box.corner is box2.corner) #True
print(box.corner == box2.corner) #True

这个操作叫做浅复制 (shallow copy) ,因为它仅复制了对象以及其包含的引用,但未复制嵌套的对象。

怎么办呢?copy 模块拥有一个叫做 deepcopy 的方法,它不仅可以复制一个对象,还可以复制这个对象所引用的对象,甚至可以复制这个对象所引用的对象 所引用的对象,等等。没错!这个操作叫做深复制 (deep copy) 。

box2 = copy.deepcopy(box)

print(box) # <__main__.Rectangle object at 0x0000015F1C97BD60>
print(box2) #<__main__.Rectangle object at 0x0000015F1C97B2B0>
print(box is box2) #False
print(box == box2) #False
print(box.corner is box2.corner) #False
print(box.corner == box2.corner) #False

循环引用

注意,一般来说,深复制不是件简单的事。如果对象有循环引用,那么这个朴素的算法会进入无限循环。deepcopy 函数会记住已经复制的对象,因此能优雅地处理循环引用

a = [10, 20]
b = [a, 30]
a.append(b)
print(a)  #[10, 20, [[...], 30]]
print(b)  # [[10, 20, [...]], 30]


from copy import  deepcopy
c = deepcopy(a)
print(c)  #[10, 20, [[...], 30]]

在这里插入图片描述
此外,深复制有时可能太深了。例如,对象可能会引用不该复制的外部资源或单例值。我们可以实现特殊方法 //copy_() 和 //deepcopy_(),控制 copy 和 deepcopy 的行为

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值