首页 > 其他分享 >第一篇

第一篇

时间:2024-11-13 09:42:00浏览次数:1  
标签:第一篇 数据类型 对象 print 拷贝 copy id

python:深拷贝与浅拷贝
一、了解几个概念
变量:是一个系统表的元素,拥有指向对象的连接空间

对象:被分配的一块内存,存储所代表的值

引用:是自动形成的从变量到对象的指针

类型:属于对象,而非变量

不可变对象:一旦创建就不可修改的对象(值内存地址固定后不可以再修改其值),包括字符串、元组、数值类型(整型、浮点型)、布尔类型

该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。

可变对象:可以修改的对象(值内存地址固定后还可以修改其值),包括列表、字典、集合
该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的地址,通俗点说就是原地改变。

当我们写:

a = 'python'
① 创建变量 a (栈)

② 创建一个对象(分配一块内存空间),来存储值“python” (数据区)

③ 将变量与对象,通过指针连接起来,从变量到对象的连接称之为引用(变量引用对象)

image-20231019215615031

赋值:只是复制了新对象的引用,不会开辟新的内存空间(将原有数据打上新的标签)

image-20231019220233726

二、浅拷贝
拷贝就是复制操作,其拷贝的值是一样的,重点观察引用关系的变化

浅拷贝:创建新对象,其内容是原对象的引用

浅拷贝之所以称为浅拷贝,是因为它仅仅只拷贝一层,拷贝了最外围的对象本身,内部的元素都只是拷贝一个引用而已。

基本语法:

import copy
copy.copy(要拷贝的变量名称)
可变数据类型的浅拷贝:
import copy

对于可变数据类型的浅拷贝

list1 = [1, 3, 5]
list2 = copy.copy(list1)
print(list1, id(list1)) # [1, 3, 5] 2029785917952
print(list2, id(list2)) # [1, 3, 5] 2029788974656
对于简单的可变数据类型,浅拷贝相当于将原对象的值进行拷贝,需要在内存空间中开辟一块新的内存空间
image-20231019221633149

import copy

list1 = [1, 3, 5, [7, 9]]
list2 = copy.copy(list1)
print(list1, id(list1)) # [1, 3, 5, [7, 9]] 1757227908928
print(list2, id(list2)) # [1, 3, 5, [7, 9]] 1757227822720

但是对于内层[7.9],这也是一个列表,也需要占用内存地址,那么其拷贝过程的内存是否相同呢

print(id(list1[3])) # 2710364155712
print(id(list2[3])) # 2710364155712
对于复杂的可变数据类型,浅拷贝只能拷贝可变数据类型的最外层对象,而无法拷贝内层对象,所以只需要为最外层对象开辟内存空间,内层对象拷贝之后的引用关系和原对象保持不变
image-20231019222741554

结论:浅拷贝能力有限,只能拷贝最外层对象(只需要为最外层对象开辟内存空间),而无法拷贝内层对象
不可变数据类型的浅拷贝
import copy

tuple1 = (1, 3, 5)
tuple2 = copy.copy(tuple1)

print(tuple1, id(tuple1)) # (1, 3, 5) 1831160185728
print(tuple2, id(tuple2)) # (1, 3, 5) 1831160185728
对于简单的不可数据类型,由于不可变数类型地址一旦固定,其值就无法改变了,又由于浅拷贝需要把自身的对象空间赋值给另外一个对象,为了保持数据一致,只能让其指向相同的内存空间(不需要额外开辟内存空间)
image-20231019224248298

对复杂的不可变数据类型,浅拷贝也只能拷贝最外层对象,无法拷贝内层对象
浅拷贝的三种形式:
切片操作、工厂函数(数据类型转换)、copy模块中的copy函数

切片操作:list1 = list[ : ] 或 list1 = [i for i in list ]

工厂函数:list1 = list(list2)

copy函数:list1 = copy.copy(list)

浅拷贝案例:
a = [1, 3, 5, [7, 9]]
b = a[:]
a[1] = 2
a[3][1] = 10

print(b)

问b输出结果多少?

[1, 3, 5, [7, 10]]

浅拷贝小结:
1)当浅复制的值是不可变对象(字符串、元组、数值类型)时和“赋值”的情况一样,对象的id值(id()函数用于获取对象的内存地址)与浅复制原来的值相同。

2)当浅复制的值是可变对象(列表、字典、集合)时会产生一个“不是那么独立的对象”存在。有两种情况:

第一种情况:复制的对象中无复杂子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。

第二种情况:复制的对象中有复杂子对象(例如列表中的一个子元素是一个列表),如果不改变其中复杂子对象,浅复制的值改变并不会影响原来的值。 但是改变原来的值中的复杂子对象的值会影响浅复制的值。

三、深拷贝
深拷贝:与浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关系。

所以改变原有被复制对象不会对已经复制出来的新对象产生影响。

深拷贝只有一种形式:使用copy模块中的deepcopy函数

import copy
data = copy.deepcopy(data)
简单的可变类型深拷贝:

import copy

a = [1, 3, 5]
b = copy.deepcopy(a)
print(a, id(a)) # [1, 3, 5] 2434688069312
print(b, id(b)) # [1, 3, 5] 2434690765888
复杂的可变类型深拷贝:

import copy

a = [1, 3, 5, [7, 9]]
b = copy.deepcopy(a)
print(a, id(a)) # [1, 3, 5, [7, 9]] 2162810741504
print(b, id(b)) # [1, 3, 5, [7, 9]] 2162810727424
print(id(a[3])) # 2162810727040
print(id(b[3])) # 2162810727232
对于可变数据类型的深拷贝,深拷贝拷贝了所有的数据并开辟对应的内存空间储存。
不可变数据类型的深拷贝:

import copy

a = (1, 3, 5, (7, 9))
b = copy.deepcopy(a)
print(a, id(a)) # (1, 3, 5, (7, 9)) 2029346995712
print(b, id(b)) # (1, 3, 5, (7, 9)) 2029346995712

print(id(a[3])) # 2029346562432
print(id(b[3])) # 2029346562432
对于不可变数据类型的深拷贝,不管是简单的还是复杂的,深拷贝都只能对象的引用关系,他们指向了相同的内存空间
四、深浅拷贝中的特殊情况
案例1:可变嵌套不可变数据类型

import copy

a = [1, 3, 5, (7, 9)]
b = copy.copy(a)
c = copy.deepcopy(a)
print(id(a)) # 1186799997824
print(id(b)) # 1186799988352
print(id(c)) # 1186799988672

print(id(a[3])) # 1700778811776
print(id(b[3])) # 1700778811776
print(id(c[3])) # 1700778811776

外层对象是可变数据类型,所以可以进行完全拷贝(需要生成内存空间),但是内层对象是不可变数据类型,所以只能进行拷贝引用关系
案例2:不可变嵌套可变数据类型

d = (1, 3, 5, [7, 9])
e = copy.copy(d)
f = copy.deepcopy(d)

print(id(d)) # 2193468143712
print(id(e)) # 2193468143712
print(id(f)) # 2193468262576 内存地址不一样了

print(id(d[3])) # 2764873530496
print(id(e[3])) # 2764873530496
print(id(f[3])) # 2764873530560
如果一个不可变数据类型包含了可变数据类型,浅拷贝的结论与之前一致,都只能拷贝引用关系。但是对于深拷贝而言,这种数据类型整体都可以进行完全拷贝
image-20231020165800762

标签:第一篇,数据类型,对象,print,拷贝,copy,id
From: https://www.cnblogs.com/bighan1/p/18543175

相关文章

  • Scrum冲刺-第一篇
    这个作业属于哪个课程广工计院计科34班这个作业要求在哪里作业要求这个作业的目标小队各成员的任务确认和项目期望一、团队简介:队名:edg.gdut成员:姓名学号罗祖文3121004537郑志涛3122004547陈恺麟3122004515许凌铎3122004540邓茗骏......
  • 【Syncfusion系列】Diagram 杂谈第一篇
    前言我认为Diagram是Syncfusion中首屈一指的优秀控件!最近在写一个工作流引擎,前端界面就用的是Diagram,接下来就来看一看。Diagram的事件查看SfDiagram的属性,如果想实现什么事件,就看这些Command结尾的。这里,我想看看双击Diagram中的一个节点,能触发什么事件。于......
  • 存储过程(第一篇)
    今天为大家带来的是MySQL的存储过程讲解与运用,这篇文章主要是为了带大家存储过程的引用与理解。存储过程是什么?存储过程(StoredProcedure)是数据库中一组为了完成特定功能的SQL语句集,这些语句集被编译并保存在数据库中,用户可以通过调用存储过程的名字并传递参数(如果有的话)来执......
  • MySQL高级语言(第一篇)
    1.SQL高级语言是什么?SQL(StructuredQueryLanguage)本身是一种用于管理和操作关系数据库的标准编程语言,它并不是传统意义上的“高级语言”如C++、Java或Python等。然而,在数据库管理和开发的语境中,人们有时会提到“SQL高级语言”或“SQL的高级特性”,这通常指的是SQL中那些更为复......
  • 如何看懂sa-token 第一篇 是怎么设计被spring加载的
    2023年入职了一家公司,他们给到我这边的系统架构我看基本都用到了sa-token,抱着去学习的态度去官网看了文档Sa-Token,感觉有些头大,摸不着头脑,然后尝试去下载源码来看gitclonehttps://gitee.com/dromara/sa-token.git我看的时候,最新版本是v1.39.0,代码一大推,实在看不明白看懂源......
  • 2024码上启程!-我的第一篇博客
     CSDN的博客朋友们大家好,我是一个对编程充满热情的大二学生,由软件工程专业出身,步入大学以来一直对科技和代码有着浓厚的兴趣,今后我会将我所写的代码和在大学期间所学的编程知识,遇到的编程问题,以博客的形式发布到网上,希望和大家一起探讨,学习,解决一些编程上的问题。一、编程......
  • 第一篇博客
    我是山东农业大学大一学生,我的专业是动物科学,但是我不是很喜欢这个,只是分数低才选到它。我想要大二转到电气工程及其自动化专业,应该需要学习c语言,c++,CAD等虽然感觉比较陌生,但这样才会有专业壁垒,且国家电网应该蛮不错的,比较稳定。现在在从网上学习C语言,我想着先学会这个。c++......
  • 程序员修炼之道——从小工到专家,读后感第一篇
    现在只完成了,第一章——注重实效的哲学的阅读,其中,“注重实效的哲学”给我留下了深刻的印象。在这章中,作者强调了实际效果的重要性,提醒我们在编程和工作中,不仅要追求理论知识和技术的深度,更要关注产出的价值和实际应用。这一观点让我意识到,作为一个程序员,不仅仅是要掌握各种编程......
  • GraphRAG原理及部署实战(GraphRAG系列第一篇)
        RAG在大模型时代,被寄予了厚望,但在近一年多各大小公司的实施过程中,其效果远没有抖音中宣传的那么振奋人心,其原因是多方面的。这篇文章就RAG中的一个弱项--局部性来展开讨论。一、RAG原理       图1描述了RAG的原理,用户输入了一个指令Instruct,RAG将其与Docu......
  • Autofac 解释第一个例子 《第一篇》
    Autofac解释第一个例子《第一篇》 Autofac是一个轻量级的依赖注入的框架,同类型的框架还有Spring.NET,Unity,Castle等。Autofac的使用有一个非常让人郁闷的地方,就是服务器要求安装有Microsoft.NETFramework4KB2468871。该补丁的地址是:http://www.microsoft.com/zh-cn......