Python浅拷贝与深拷贝(可变对象与不可变对象)
第一次遇到深拷贝和浅拷贝的问题是用python在一个for循环中对一个list赋值,使用的语句是
a = b
这个b会不断带入循环,每次计算得到,最后发现list乱七八糟的,后来才发现,python中a=b并不是创建一个a,将b的值赋给它,而是b的地址的一个复制。
后来其实在C#的另一个问题中,也是类似的错误,当然这个问题要复杂的多,因此特地重写了一篇。
1、首先明白什么是浅拷贝和深拷贝
- 可变对象、不可变对象
在了解深浅拷贝之前,先了解可变与不可变对象。
可变对象是指地址所指向的值可变的对象,相反不可变对象就是地址所指向的对象是不可变的。举个例子
a = 5
a = 6
这里a赋了两次值,但是第二次赋值时,a的地址并不改变,不同的是,执行语句后,a指向的地方存储的值从5变成了6。因此,a是可变对象。
那么,这里的‘5’呢?5是一个数字,当你在语句中输入5时,它就一定代表数字5所在的地方,你不能给5再赋值,因此,5是不可变对象。当你试图改变一个不可变对象时,它事实上新生成了一个对象并返回给你。例如,y = y+1,返回的y事实上已经不是原来y的地址了。
不可变对象包含:int,float,complex,long,str,unicode,tuple。
- 深浅拷贝语句
import copy
a=[1,2,3,4,5,['a','b']]
#原始对象
b=a#赋值,传对象的引用
c=copy.copy(a)#对象拷贝,浅拷贝
d=copy.deepcopy(a)#对象拷贝,深拷贝
print "a=",a," id(a)=",id(a),"id(a[5])=",id(a[5])
print "b=",b," id(b)=",id(b),"id(b[5])=",id(b[5])
print "c=",c," id(c)=",id(c),"id(c[5])=",id(c[5])
print "d=",d," id(d)=",id(d),"id(d[5])=",id(d[5])
print "*"*70 a.append(6)#修改对象a
a[5].append('c')#修改对象a中的['a','b']数组对象
print "a=",a," id(a)=",id(a),"id(a[5])=",id(a[5])
print "b=",b," id(b)=",id(b),"id(b[5])=",id(b[5])
print "c=",c," id(c)=",id(c),"id(c[5])=",id(c[5])
print "d=",d," id(d)=",id(d),"id(d[5])=",id(d[5])
a = b 是赋值操作,只是复制一个地址,就是一个地址的两个引用。
copy函数是另起了一个地址,存放新对象,但是只是重新复制了可变对象,不可变对象的地址并不变,因此如果改动不可变对象,copy出的对象仍然会变。
deepcopy则是完完全全生成一个新的对象,源对象中的所有对象都生成一份放在新地址中。
2、为什么要有深浅拷贝
其实我一直在用相关的知识,但一直不自知,没有去仔细思考。例如,在一道leetcode题中:
#21.合并两个有序链表
#将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
class Solution(object):
def mergeTwoLists(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
p = ListNode(0)
ans = p
while(l1 or l2):
if(l1==None):
p.next = l2
return ans.next
elif(l2==None):
p.next = l1
return ans.next
else:
if(l1.val>l2.val):
p.next = l2
p = p.next
l2 = l2.next
else:
p.next = l1
p = p.next
l1 = l1.next
return ans.next
标黄的一行,就是用ans记录p节点的位置,这样p就可以进行迭代,最终返回ans,即返回了头指针,在这里,很容易理解,ans = p操作就是使用ans复制了p的地址,并不是复制p的值,也不是指向p,而是,执行了将p所在的地址引用给ans。这就是浅拷贝。
- a = b赋值语句通常用来记录b的地址,如果b需要在后续进行迭代操作,a可以记录初始位置。
- copy函数进行了浅层拷贝,可以在新对象中对一些元素进行操作和修改,同时,那些常用的不可变对象还是原来的元素,这样的好处至少如下
- 时间、内存开销更小,效率更高了
- 用编程思想来类比,不可变对象可以认为类似于全局变量,可变对象可以认为是实例化对象中的局部变量,这样做的好处就显而易见了
- deepcopy就是完全全新生成了一个对象,其中所有的对象都是全新,与源对象没关系的,可以随意更改。
3、函数中的传参关系
我们常常把一个对象传入到函数中,函数中也经常对该对象进行操作。虽然有不同的赋值操作,但是其实不同操作之间的含义是不一样的。
def func1(nums):
nums = set(nums) def func2(nums):
nums = [1,2,3,4,5]
l = [1,1,1,1]
func1(l)
func2(l)
在这个操作中,都在定义的函数中对nums进行了赋值,但是如果实操就会发现,在函数内nums被改变了,但函数运行结束后,l还是等于[1,1,1,1]。
这是因为在函数中,nums这个对象代表的只是对l的引用,nums = set(nums)这个语句,是生成了一个set(nums)对象,并将该对象的地址赋给了nums这个引用。并没有将set(nums)的内容覆盖掉nums所在的地址的内容。
def func3(nums):
for i in range(len(nums)):
nums[i] = 9 def func4(nums):
nums.pop(1)
nums.append(3) l = [1,1,1,1]
func3(l)
func4(l)
在这个操作中,会发现l的值都被改变了。这是因为以上无论是针对index赋值还是list操作,都是针对nums这个变量所指向的list对象进行的操作,也就是实实在在地操作在了地址上,改变了元素。这事实上是python函数中传参的问题。有一篇博文写的很清楚:python函数参数传递:传值还是传引用。
所以要清楚地记得,python函数中传递的就是对象的地址。当操作针对地址所指的对象进行操作时,就会变,否则当你试图给这个传递的参数赋值时,就会失效。
Python浅拷贝与深拷贝(可变对象与不可变对象)的更多相关文章
- python 浅拷贝和深拷贝(9)
何谓浅拷贝/深拷贝,说得直白一点,其实就是数据拷贝,两者到底有什么区别呢?听着就挺迷糊的,python开发项目的时候说不定你就能碰上这样的坑~~ 一.普通的变量赋值 我们平常使用的变量赋值就是 ...
- python浅拷贝和深拷贝
博文参考地址:https://blog.csdn.net/qq_20084101/article/details/82925067 最近在撸码的时候发现了一个严重的问题: a = [1,2] c = ...
- python浅拷贝与深拷贝
今天写程序,人为制造了一个由浅拷贝引起的bug,有必要归纳一下.先附上源代码: class PerformanceTest(object): def __init__(self): ....... s ...
- python浅拷贝与深拷贝浅析
首先我们要明确,python中大多数都是浅拷贝,我们先说原因: 1.时间花费更少 2.内存更小 3.效率更高,浅拷贝只拷贝顶层数据,一般情况下比深拷贝效率高. 容器(如列表)切片是浅拷贝
- Python - 对象赋值、浅拷贝、深拷贝的区别
前言 Python 中不存在值传递,一切传递的都是对象的引用,也可以认为是传址 这里会讲三个概念:对象赋值.浅拷贝.深拷贝 名词解释 变量:存储对象的引用 对象:会被分配一块内存,存储实际的数据,比如 ...
- Python中赋值、浅拷贝和深拷贝的区别
前言文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取http: ...
- 学习Python一年,这次终于弄懂了浅拷贝和深拷贝
官方文档:copy主题 源代码: Lib/copy.py 话说,网上已经有很多关于Python浅拷贝和深拷贝的文章了,不过好多文章看起来还是决定似懂非懂,所以决定用自己的理解来写出这样一篇文章. 当别 ...
- 我的Python学习笔记(二):浅拷贝和深拷贝
在Python中,对象赋值,拷贝(浅拷贝和深拷贝)之间是有差异的,我们通过下列代码来介绍其区别 一.对象赋值 对象赋值不会复制对象,它只会复制一个对象引用,不会开辟新的内存空间 如下例所示,将test ...
- Python 赋值、浅拷贝和深拷贝
初学Python,和C++还是有许多不同.直接赋值.浅拷贝和深拷贝,这三种拷贝对象的操作之间还是有许多的区别.Python语言的版本为2.7,在Pycharm中进行实验. 一.直接赋值 用下面的代码来 ...
随机推荐
- Django 中 cookie的使用
Cookie是浏览器在客户端留下的一段记录,这段记录可以保留在内存或者硬盘上.因为Http请求是无状态的,通过读取cookie的记录,服务器或者客户端可以维持会话中的状态.比如一个常见的应用场景就是登 ...
- Python把多行文本合并
在引用论文时,往往格式出错,出现非常多行,这样操作非常不方便.这种方法讲多行合并之后,再处理: # 文件空格和回车键处理工具infile = r'C:\Users\SAM\Desktop\新建文本文档 ...
- python面向对象基础(三)内置方法 __xx__
__str__和__repr__,__format__ 改变对象的字符串显示__str__,__repr__ 自定制格式化字符串__format__ #_*_coding:utf-8_*_ forma ...
- 关于redis的主从、哨兵、集群(转)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/c295477887/article/de ...
- gtid 1032错误案例
gtid 1032错误案例 大致背景: 分别在主从上删除了系统冗余账号. mysql> delete from mysql.user where host='::1';Query OK, 1 r ...
- JVM(六),java内存模型
六.java内存模型 1.线程独占部分 (1)程序计数器 (2)Java虚拟机栈 (3)本地方法栈 (4)递归为什么会引发java.lang.StackOverFlowError异常吗 2.线程共享部 ...
- Web上传超大文件解决方案
文件上传下载,与传统的方式不同,这里能够上传和下载10G以上的文件.而且支持断点续传. 通常情况下,我们在网站上面下载的时候都是单个文件下载,但是在实际的业务场景中,我们经常会遇到客户需要批量下载的场 ...
- CSS3 box-sizing:content-box | border-box
box-sizing:content-box | border-box 默认值:content-box 适用于:所有接受width和height的元素 继承性:无 content-box: paddi ...
- python测试网站访问速度
# -*- coding: utf-8 -*- # @Author : Felix Wang # @time : 2018/8/13 22:13 # pip3 install pycurl impor ...
- 90%的人说Python程序慢,5大神招让你的代码像赛车一样跑起来
1.for 循环 我们大部分的时候代码里面都有for循环,然后里面嵌套一段逻辑处理,下面有两种方法来完成: 二者的性能差距有多大呢,一般我们用内置的timeit模块来量化比较: 把传统的for改成推导 ...