前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python深度拷贝也不是完美的

Python深度拷贝也不是完美的

作者头像
somenzz
发布2020-11-25 14:46:05
1.1K0
发布2020-11-25 14:46:05
举报
文章被收录于专栏:Python七号

Python的浅拷贝和深拷贝的区别,相信你已经非常熟悉了,浅拷贝就是对原对象重新申请一个内存空间,但原对象的子对象如果是可变对象,仍然是存在引用关系的;深拷贝也是重新申请内存空间,以递归的方式,通过创建新的子对象拷贝到新对象中,无论是原对象还是其子对象都是彼此独立的,新对象和原对象没有任何关联。

不过,深度拷贝也不是完美的,先看段代码,你可以先预测下程序的输出,然后在执行下,看看预期是否一致。

代码语言:javascript
复制
import copy
x = [1]
x.append(x)
y = copy.deepcopy(x)

# 以下命令的输出是?
x == y

程序执行到第 3 行时,x 已经是一个无限嵌套的列表,但是,执行到第 4 行时,程序进行了深度拷贝,就会递归的创建新的子对象,却并没有发生内存溢出的错误,这是为什么呢?

其实,这是因为深度拷贝函数 deepcopy 中会维护一个字典,记录已经拷贝的对象与其 ID。拷贝过程中,如果字典里已经存储了将要拷贝的对象,则会从字典直接返回,我们来看相对应的源码就能明白:

代码语言:javascript
复制
def deepcopy(x, memo=None, _nil=[]):
    """Deep copy operation on arbitrary Python objects.
      
  See the module's __doc__ string for more info.
  """
  
    if memo is None:
        memo = {}
    d = id(x) # 查询被拷贝对象x的id
  y = memo.get(d, _nil) # 查询字典里是否已经存储了该对象
  if y is not _nil:
      return y # 如果字典里已经存储了将要拷贝的对象,则直接返回
        ...

程序执行到第 7 行比较两个对象的值是否相等时却会报错,这是什么原因呢?

因为 x 是一个无限嵌套的列表,y 深拷贝于 x,按道理来讲 x == y 应该是 True 的,但进行比较操作符 == 的时候,== 操作符则会递归地遍历对象的所有值,并逐一比较。而 Python 为了防止栈崩溃,递归的层数是要限定的,不会无休下去,所以到了限定的层数,Python 解释器会跳出错误:

代码语言:javascript
复制
>>> import copy
>>> x=[1]
>>> x.append(x)
>>> x
[1, [...]]
>>> y = copy.deepcopy(x)
>>> x == y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RecursionError: maximum recursion depth exceeded in comparison
>>>

其原因也是 Python 的递归层数是有限定的,在 sys 模块中有个方法可以得到递归的层数:

代码语言:javascript
复制
>>> import sys
>>> sys.getrecursionlimit()
1000

当然你也可以重新设定递归的层数:

代码语言:javascript
复制
>>> import sys
>>> sys.getrecursionlimit()
1000
>>> sys.setrecursionlimit(10000)
>>> sys.getrecursionlimit()
10000

那是不是可以设定无穷大呢?理论上可以,但你的程序崩溃也是一定的,你自己可以做个测试。

总结一下,深度拷贝的缺点在于如果对象内存在指向自身的引用,那么很容易出现无限循环,而引用、浅拷贝却不会,如下:

代码语言:javascript
复制
>>> import copy
>>> x=[1]
>>> x.append(x)
>>> x_alias = x
>>> x_copy = copy.copy(x)
>>> x_deepcopy = copy.deepcopy(x)
>>> x == x_alias
True
>>> x == x_copy
True
>>> x == x_deepcopy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RecursionError: maximum recursion depth exceeded in comparison
>>>
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python七号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档