首页 > 其他分享 >浅拷贝和深拷贝

浅拷贝和深拷贝

时间:2023-08-19 17:36:35浏览次数:26  
标签:对象 key print 拷贝 copy id

前言

面试的时候经常会问到深拷贝和浅拷贝,那么python的深拷贝和浅拷贝有什么区别呢?

思考题

先来看 2 个简单的案例, 对元素 a/aa 重新赋值一个新的变量 b/bb 后,改变原来 a/aa 的值,看会不会影响新的变量 b/bb 的值

# 1.str
a = "hello"
b = a
a = "world"
print('a: {}'.format(a))
print('b: {}'.format(b))

# 2.list
aa = [1, 2, 3]
bb = aa
aa.append(4)
print('aa: {}'.format(aa))
print('bb: {}'.format(bb))

运行结果

a: world
b: hello
aa: [1, 2, 3, 4]
bb: [1, 2, 3, 4]

这是个很有趣的事情,字符串重新赋值给b后,改变原来a的值,b不会跟着变。
但是list重新赋值给bb后,改变aa的值,bb的值也跟着变了。
这里有个知识点:在python中,都是将“对象的引用(内存地址)”赋值给变量的。其次,在python中有6个标准数据类型,他们分为可变和不可变两类。

可变和不可变对象

在python中有6个标准数据类型,他们分为可变和不可变两类。

  • 不可变类型:Number(数字)String(字符串)Tuple(元组)
  • 可变类型:List(列表)Dictionary(字典)Set(集合)

可变对象和不可变对象的内存地址可以通过id函数获取

  • 可变对象:可变对象可以在其 id() 保持固定的情况下改变其取值;
  • 不可变对象:具有固定值的对象。不可变对象包括数字、字符串和元组。这样的对象不能被改变。如果必须存储一个不同的值,则必须创建新的对象。
  • id(object): 函数用于获取对象的内存地址,函数返回对象的唯一标识符,标识符是一个整数。

字符串和数字都是不可变类型,不同变量赋值一样,通过id获取的内存地址是一样的

# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/


a = "abc"
b = "abc"

print(id(a))
print(id(b))
print(a is b)

c = 100
d = 100
print(id(c))
print(id(d))
print(c is d)

运行结果

1557212603592
1557212603592
True
1561032832
1561032832
True

list、dict 和 set集合是可变类型,虽然值一样,但是id获取的内存地址不一样

# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/


a = {"key": "123"}
b = {"key": "123"}

print(id(a))
print(id(b))
print(a is b)
print(a == b)

c = [1, 2, 3]
d = [1, 2, 3]
print(id(c))
print(id(d))
print(c is d)
print(c == d)

运行结果

1638920310144
1638920310216
False
True
1638921292360
1638921292680
False
True

现在知道了id函数获取内存地址,我们说的深拷贝和浅拷贝是针对可变对象:list、dict 和 set集合

copy模块

python 中的深拷贝和浅拷贝使用 copy 模块

浅拷贝 A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.

上面这段话是官方文档上的描述,有2个含义:

  • 1.浅拷贝会创建一个新的容器对象(compound object)
  • 2.对于对象中的元素,浅拷贝就只会使用原始元素的引用(内存地址)

常见的浅拷贝操作有:

  • 使用切片操作[:]
  • 使用工厂函数(如list/dict/set)
  • copy模块的copy()方法

深拷贝 A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

上面这段话是官方文档上的描述,也是有2个含义:

  • 1.深拷贝和浅拷贝一样,都会创建一个新的容器对象(compound object)
  • 2.和浅拷贝的不同点在于,深拷贝对于对象中的元素,深拷贝都会重新生成一个新的对象

浅拷贝

浅拷贝使用 copy 模块的 copy 方法

# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/
import copy


a = [1, "hello", [2, 3], {"key": "123"}]

b = copy.copy(a)

print(id(a))    # 外面容器拷贝了,所以a和b的id不一样
print(id(b))

# a和b容器里面的元素对象id
print(id(a[2]))
print(id(b[2]))

运行结果

1340977220424
1340977221576
1340977220168
1340977220168

浅拷贝是拷贝了list外面一层的, 创建一个新的容器对象(compound object),所以a和b的id是不一样的
对于容器里面的元素对象,浅拷贝就只会使用原始元素的引用(内存地址),所以可以看到子元素的内存地址还是一样的

如果改变a里面的不可变对象数字和字符串,此时a和b的值就不一样了,但是b的后面没改变的元素还是指向a

# 改变a的 数字和字符串对象
a[0] = 2

# a 和b 的值不一样了
print(a)
print(b)

# 但是后面的元素还是指的a
print(id(a[2]))
print(id(b[2]))

运行结果

[2, 'hello', [2, 3], {'key': '123'}]
[1, 'hello', [2, 3], {'key': '123'}]
2488134044232
2488134044232

如果改变a里面的可变对象, 把[2, 3]里面的3改成 [2, 4]

# 改变a的 可变对象 [2, 4]
a[2][1] = 4

print(a)
print(b)

print(id(a[2]))
print(id(b[2]))

运行结果

[1, 'hello', [2, 4], {'key': '123'}]
[1, 'hello', [2, 4], {'key': '123'}]
2385125673544
2385125673544

此时b会随着a的改变而改变,这就是浅拷贝了

深拷贝

浅拷贝使用 copy 模块的 deepcopy 方法

import copy
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/


a = [1, "hello", [2, 3], {"key": "123"}]

b = copy.deepcopy(a)

print(id(a))    # 外面容器拷贝了,所以a和b的id不一样
print(id(b))

# a和b容器里面的元素对象id
print(id(a[2]))
print(id(b[2]))

# 改变a的 可变对象 [2, 4]
a[2][1] = 4

print(a)
print(b)

print(id(a[2]))
print(id(b[2]))

深拷贝和浅拷贝的不同点在于,深拷贝对于对象中的元素,深拷贝都会重新生成一个新的对象。
所以不管a怎么变,都不会影响b的值

赋值

赋值跟浅拷贝 深拷贝是有区别的,可以看下面的示例

# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/


a = [1, "hello", [2, 3], {"key": "123"}]

b = a
print(id(a))
print(id(b))

# a和b容器里面的元素对象id
print(id(a[2]))
print(id(b[2]))

a[0] = 2
print(a)
print(b)

运行结果

1992198687560
1992198687560
1992198687304
1992198687304
[2, 'hello', [2, 3], {'key': '123'}]
[2, 'hello', [2, 3], {'key': '123'}]

赋值语句并没有生成新的容器,跟浅拷贝的区别在于外面的容器也是指向的a的内存地址,并没有生成新的容器

参考博客资料https://www.nowcoder.com/discuss/203654?type=2&order=0&pos=1232&page=0
参考博客资料https://copyfuture.com/blogs-details/2020031720252559878eggumgw4iaj7c

标签:对象,key,print,拷贝,copy,id
From: https://www.cnblogs.com/echotest/p/17642753.html

相关文章

  • winform编译时怎么把指定dll拷贝到debug里面(非引用)
    选中winform项目-》右键属性-》选择生成事件-》在生成后事件命令行配置以下代码Copy"$(ProjectDir)dll\*.*""$(ProjectDir)$(OutDir)" ......
  • 8.1 C++ STL 变易拷贝算法
    C++STL中的变易算法(ModifyingAlgorithms)是指那些能够修改容器内容的算法,主要用于修改容器中的数据,例如插入、删除、替换等操作。这些算法同样定义在头文件<algorithm>中,它们允许在容器之间进行元素的复制、拷贝、移动等操作,从而可以方便地对容器进行修改和重组。主要包括以下......
  • 8.1 C++ STL 变易拷贝算法
    C++STL中的变易算法(ModifyingAlgorithms)是指那些能够修改容器内容的算法,主要用于修改容器中的数据,例如插入、删除、替换等操作。这些算法同样定义在头文件<algorithm>中,它们允许在容器之间进行元素的复制、拷贝、移动等操作,从而可以方便地对容器进行修改和重组。主要包括以下......
  • 对象拷贝方法
    C#中4种深拷贝方法介绍概述为什么要用到深拷贝呢?比如我们建了某个类Person,并且实例化出一个对象,然后,突然需要把这个对象复制一遍,并且复制出来的对象要跟之前的一模一样,来看下我们一般会怎么做。1、利用反射实现publicstaticTDeepCopy(Tobj){//如果是字符串或值类型则......
  • Python 对象拷贝的详细教程
    在本篇文章中,会先介绍Python中对象的基础概念,之后会提到对象的深浅拷贝以及区别。在阅读后,应该掌握如下的内容:理解变量、引用和对象的关系理解Python对象中identity,type和value的概念什么是mutable和immutable对象?以及它们和hashable的关系深浅拷贝的过程以及区别1.......
  • 解读 --- 对象的深拷贝
    合集-c#基础(12) 1.编码技巧---如何实现字符串运算表达式的计算07-122.编码技巧---同步锁对象的选定07-133.编码技巧---使用dynamic简化反射07-244.编码技巧---谨防闭包陷阱07-195.并发编程---信号量线程同步07-186.并发编程---为何要线程池化07-187.并发编程......
  • 解读 --- 深拷贝
    引言深拷贝是指创建一个新对象,该对象的值与原始对象完全相同,但在内存中具有不同的地址。这意味着如果您对原始对象进行更改,则不会影响到复制的对象常见的C#常见的深拷贝方式有以下4类:各种形式的序列化及反序列化。通过反射机制获取该对象的所有字段和属性信息。遍历所有字段......
  • Python 对象拷贝的详细教程
    在本篇文章中,会先介绍Python中对象的基础概念,之后会提到对象的深浅拷贝以及区别。在阅读后,应该掌握如下的内容:理解变量、引用和对象的关系理解Python对象中identity,type和value的概念什么是mutable和immutable对象?以及它们和hashable的关系深浅拷贝的......
  • Javascript学习笔记-js实现拷贝复制功能
    /***复制单行内容到粘贴板*content:需要复制的内容*message:复制完后的提示,不传则默认提示"复制成功"*/functioncopyToClip(content,message){varaux=document.createElement("input");aux.setAttribute("value"......
  • js 深拷贝
    转自:https://github.com/shfshanyue/Daily-Question/issues/203#issuecomment-888238489/***深拷贝关注点:*1.JavaScript内置对象的复制:Set、Map、Date、Regex等*2.循环引用问题*@param{*}object*@returns*/functiondeepClone(source,memory){c......