首页 > 编程语言 >【Python】python深拷贝和浅拷贝(一)

【Python】python深拷贝和浅拷贝(一)

时间:2023-01-28 16:02:00浏览次数:64  
标签:__ reductor Python memo python 对象 deepcopy 拷贝

【Python】python深拷贝和浅拷贝(一)

大家好,我们的gzh是朝阳三只大明白,满满全是干货,分享近期的学习知识以及个人总结(包括读研和IT),跪求一波关注,希望和大家一起努力、进步!!

定义

  • 直接赋值:其实就是对象的引用。
  • 浅拷贝:拷贝父对象,不会拷贝对象的内部的子对象。
  • 深拷贝: copy 模块的 ​​deepcopy​​ 方法,完全拷贝了父对象及其子对象。

浅拷贝:构造一个新的对象,尽可能的将原始对象中的所有找到的对象引用加入到新构造的对象中;

深拷贝:构造一个新的对象,然后递归的在原始对象中将找到的对象的副本插入其中。

  • 有可能由于循环引用,可能会导致递归循环;
  • 深层次有可能会复制过多的对象;

示例

直接赋值

arr = [1, 2, 3, 4]
arr1 = arr
id(arr), id(arr1)
---------------------
(2226394970240, 2226394970240)

结论:直接赋值只是将对对象的引用直接给了新的变量,没有在堆中构建一个新的对象。

【Python】python深拷贝和浅拷贝(一)_深拷贝

浅拷贝

使用​​copy.copy()​​进行浅拷贝,浅复制可以理解为只对最高层对象进行一个复制,对其它层的对象只是引用。

list1 = [[1, 2, 3], [4, 5, 6]]
list2 = copy(list1)
print(id(list1), id(list2))
print(id(list1[0]), id(list2[0]))
print(id(list1[1]), id(list2[1]))

运行结果如下,可以发现list1list2的内存地址并不相同,但是他们内部元素的地址是相同的,也证明了浅拷贝只对最高层对象进行复制,不会对子对象进行复制。

【Python】python深拷贝和浅拷贝(一)_深拷贝_02

画图来看,​​copy​​方法只是构建了一个新的容器,其内部对堆中对象的引用被复制了,但是堆中的对象没有任何操作。

【Python】python深拷贝和浅拷贝(一)_Python_03


list1 = [[1, 2, 3], [4, 5, 6]]
list1.append("append")
print(list1, list2)

结果如下,证明最高层对象是真正在堆中创建了一个新的对象。

【Python】python深拷贝和浅拷贝(一)_Python_04

画图来看,​​list1​​和​​list2​​已经不是一个对象了。

【Python】python深拷贝和浅拷贝(一)_Python_05

上面的示例中,我们已经证明了对浅拷贝对象本身的操作是不会相互影响的。现在再看另一个示例

arr = [{1, 2, 3}, "aba", 4, {'a': 1, 'b': 2}]
arr1 = arr[:]

for i, j in zip(arr, arr1):
print(id(i) == id(j))

【Python】python深拷贝和浅拷贝(一)_浅拷贝_06

从上面的示例可以看到,采用浅拷贝复制对象后,对象内部的所有子对象本质上都是同一个,那么对它们的操作必定会相互影响。

arr1[0].add(4)
arr1[3]['c'] = 3
arr, arr1

【Python】python深拷贝和浅拷贝(一)_Python_07

深拷贝

深拷贝可以理解为多层嵌套浅拷贝,需要调用​​deepcopy​​进行复制,示例如下:

list1 = [[1, 2, 3], [4, 5, 6], 7, "a"]
list2 = deepcopy(list1)
for i, j in zip(list1, list2):
print(id(i), id(j))

【Python】python深拷贝和浅拷贝(一)_深拷贝_08

可以看到,复制之后​​[1, 2, 3], [4, 5, 6]​​的地址已经不在相同,代表这两个对象都已经在堆中重新创建了一份。画图来看:

【Python】python深拷贝和浅拷贝(一)_Python_09

​'a'​​以及​​7​​的引用是相同的,原因在于这些基础数字、字母在python启动的时候会进行缓存,在内存中有且只有一份。

源码分析

copy方法

def copy(x):
"""Shallow copy operation on arbitrary Python objects.

See the module's __doc__ string for more info.
"""

# 获取要复制对象的类型
cls = type(x)

# 获取对象的copier,如果copier不为空则使用它进行复制,本质上来说copier是一个Function对象。
copier = _copy_dispatch.get(cls)
if copier:
return copier(x)

# 如果要复制的对象是由元类(MetaClass)创造的即类(class),那么他的处理方式和不可变对象一致。
if issubclass(cls, type):
# treat it as a regular class:
return _copy_immutable(x)

# 如果对象的类实现了__copy__方法是调用该方法进行复制
copier = getattr(cls, "__copy__", None)
if copier is not None:
return copier(x)

# 使用序列化的方式进行复制,同样是先查看是否有现有的
reductor = dispatch_table.get(cls)
if reductor is not None:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor is not None:
rv = reductor(4)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error("un(shallow)copyable object of type %s" % cls)

if isinstance(rv, str):
return x
return _reconstruct(x, None, *rv)
# 定义了那些类型使用deepcopy_atomic作为copier
d[type(None)] = _deepcopy_atomic
d[type(Ellipsis)] = _deepcopy_atomic
d[type(NotImplemented)] = _deepcopy_atomic
d[int] = _deepcopy_atomic
d[float] = _deepcopy_atomic
d[bool] = _deepcopy_atomic
d[complex] = _deepcopy_atomic
d[bytes] = _deepcopy_atomic
d[str] = _deepcopy_atomic
d[types.CodeType] = _deepcopy_atomic
d[type] = _deepcopy_atomic
d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic
d[weakref.ref] = _deepcopy_atomic
d[property] = _deepcopy_atomic

# 针对容器类型,在builtsin.py中定义了他们复制方式
d[list] = list.copy
d[dict] = dict.copy
d[set] = set.copy
d[bytearray] = bytearray.copy

# deepcopy_atomic会直接返回原始对象,这就是为什么int、str这种类型调用copy方法会返回原始对象
def _deepcopy_atomic(x, memo):
return x

deepcopy方法

# 循环递归进行深拷贝,用dispatch_table保存类型:复制方法,判断每一个对象的类型并找到它的深拷贝方法。
# 利用memo放置循环引用
def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects.

See the module's __doc__ string for more info.
"""

# memo用来记录复制过的对象,避免循环引用无限复制
if memo is None:
memo = {}

# 获取复制对象的id,检查memo中是否有相同的对象,如果有直接返回
d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
return y

# 整个复制流程和copy一致,不同点在于使用的复制方法不同,
cls = type(x)

copier = _deepcopy_dispatch.get(cls)
if copier is not None:
y = copier(x, memo)
else:
if issubclass(cls, type):
y = _deepcopy_atomic(x, memo)
else:
copier = getattr(x, "__deepcopy__", None)
if copier is not None:
y = copier(memo)
else:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor is not None:
rv = reductor(4)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error(
"un(deep)copyable object of type %s" % cls)
if isinstance(rv, str):
y = x
else:
y = _reconstruct(x, memo, *rv)

# If is its own copy, don't memoize.
if y is not x:
memo[d] = y
_keep_alive(x, memo) # Make sure x lives at least as long as d
return y

# 元祖深拷贝
def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
y = [deepcopy(a, memo) for a in x]
# We're not going to put the tuple in the memo, but it's still important we
# check for it, in case the tuple contains recursive mutable structures.
try:
return memo[id(x)]
except KeyError:
pass
for k, j in zip(x, y):
if k is not j:
y = tuple(y)
break
else:
y = x
return y
d[tuple] = _deepcopy_tuple

# 字典深拷贝方法
def _deepcopy_dict(x, memo, deepcopy=deepcopy):
y = {}
memo[id(x)] = y
for key, value in x.items():
y[deepcopy(key, memo)] = deepcopy(value, memo)
return y
d[dict] = _deepcopy_dict
if PyStringMap is not None:
d[PyStringMap] = _deepcopy_dict

# 列表深拷贝
def _deepcopy_list(x, memo, deepcopy=deepcopy):
y = []
memo[id(x)] = y
append = y.append
for a in x:
append(deepcopy(a, memo))
return y
d[list] = _deepcopy_list

往期回顾

​【Python】type、isinstance、issubclass详解​

文中难免会出现一些描述不当之处(尽管我已反复检查多次),欢迎在留言区指正,相关的知识点也可进行分享,希望大家都能有所收获!!

标签:__,reductor,Python,memo,python,对象,deepcopy,拷贝
From: https://blog.51cto.com/u_15945763/6024977

相关文章

  • 【Python】python深拷贝和浅拷贝(二)
    【Python】python深拷贝和浅拷贝(二)大家好,我们的gzh是朝阳三只大明白,满满全是干货,分享近期的学习知识以及个人总结(包括读研和IT),跪求一波关注,希望和大家一起努力、进步!!前言上......
  • python图片拼接
     首先我们来尝试将分片的图片复原为正常的图片这里是六张切成小细条的图片,原本是一张大图的,现在我们用python将他们合并到一块,题外话图片来源于中华连环画,*http://www......
  • 【Python学习002】函数参数
    我们的gzh是【朝阳三只大明白】,满满全是干货,分享近期的学习知识以及个人总结(包括读研和IT),希望大家一起努力,一起加油!求关注!!概述函数是组织好的、可重复使用的,用来实现单一,或......
  • 上古神兵,先天至宝,Win11平台安装和配置NeoVim0.8.2编辑器搭建Python3开发环境(2023最
    毫无疑问,我们生活在编辑器的最好年代,Vim是仅在Vi之下的神级编辑器,而脱胎于Vim的NeoVim则是这个时代最好的编辑器,没有之一。异步支持、更好的内存管理、更快的渲染速度、更......
  • python对接API二次开发高级实战案例解析:百度地图Web服务API封装函数(行政区划区域检索
    文章目录​​前言​​​​一、IP定位​​​​1.请求URL​​​​2.获取IP定位封装函数​​​​3.输出结果​​​​二、国内天气查询​​​​1.请求url​​​​2.天气查询封装......
  • colab在更换python包版本时,如何正确重启
    当我在使用Node2Vec这个包的时候,遇到了gem和numpy的版本冲突问题。最后在pc上测试,发现只要升级numpy就能解决问题。但是在colab中更新numpy版本依旧报错。后面才发现,就算在c......
  • Python爬虫实践代码示例
    对于刚入门爬虫的小伙伴来说,累积经验多练习代码是非常有必要的,下面就是有关爬虫的一些小案例,欢迎大家指正。importrequestsfrombs4importBeautifulSoup#importpandas......
  • Python入门之转义符
    """转义符:改变原始字符含义的特殊符号"""#不支持所见即所得name="黎二狗"name='黎二狗'#可见即所得name='''黎二狗'''name="""黎......
  • python入门之str/ord/chr
    """字符串(str):由一系列字符组成的不可变系列容器,存储的是字符的编码值。编码:1.字节byte:计算机最小存储单位,等于8位bit。(bitBKBMBGT)2.字符:单个的数......
  • Python中得可变哈希不可变哈希
    类型与哈希哈希(散列计算),可以将任意长度的输出,通过散列算法变为固定长度输出,简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。​​1.可哈希......