大家都是拷贝,凭什么你这么秀?

news2025/2/28 7:03:14

入门教程、案例源码、学习资料、读者群

请访问: python666.cn

大家好,欢迎来到 Crossin的编程教室 !

之前关于 Python 的作用域、赋值、参数传递,我们接连谈了几篇文章:

  • Python常见问题:全局变量和局部变量

  • Python到底是引用传递还是值传递?网上大多数教程都讲错了

  • 继续打脸水货教程:关于可变对象与不可变对象

今天我们依然要就相关话题继续下去。

首先是上次最后的思考题:

m = [1, 2, [3]]
n = m[:]
n[1] = 4
n[2][0] = 5
print(m)

m 的结果是什么?

e9cfddfd0ef834818b0059fed0e3b741.jpeg

这次比上次好点,有 35% 的正确率。5ef01af2321618dfae71be74e9c33376.png

当时我留了个提示,说和浅拷贝、深拷贝有关,现在我们就来具体说一说。

假设有这样一个 list 变量 m,其中有 4 个元素(别被嵌套迷惑了):

m = [1, 2, [3, 4], [5, [6, 7]]]

为了更直观的表示,我来画个图:

7322d8a1b5b8c25702b636048f34bfdf.jpeg

现在我们想要再来“复制”一个同样的变量。也许第一个闪过脑中的念头就是:

n = m

但看了前面的文章后你应该知道,这样的赋值只相当于增加了一个标签,并没有新的对象产生

237740d51b1dbcb0e85bcbecafc8d3cf.png

id 验证下就知道,m 和 n 仍然是同一个东西。那么他们内部的元素自然也是一样的,对其中一个进行修改,另一个也会跟着变:

m = [1, 2, [3, 4], [5, [6, 7]]]
print('m:', id(m))
print([id(i) for i in m])
n = m
print('n:', id(n))
print([id(i) for i in n])
print(n is m)
print(n[0] is m[0])
print(n[2] is m[2])
n[0] = -1
print(m)
n[2][1] = -1
print(m)

输出

m: 4564554888
[4556507504, 4556507536, 4564554760, 4564555016]
n: 4564554888
[4556507504, 4556507536, 4564554760, 4564555016]
True
True
True
[-1, 2, [3, 4], [5, [6, 7]]]
[-1, 2, [3, -1], [5, [6, 7]]]

因此有人将此操作称为“旧瓶装旧酒”,只是多贴了一层标签,这不能达到我们的目的。

要得到一个对象的“拷贝”,我们需要用到 copy 方法:

from copy import copy
m = [1, 2, [3, 4], [5, [6, 7]]]
print('m:', id(m))
print([id(i) for i in m])
n = copy(m)
print('n:', id(n))
print([id(i) for i in n])
print(n is m)
print(n[0] is m[0])
print(n[2] is m[2])
n[0] = -1
print(m)
n[2][1] = -1
print(m)

输出

m: 4340253832
[4333009264, 4333009296, 4340253704, 4340253960]
n: 4340268104
[4333009264, 4333009296, 4340253704, 4340253960]
False
True
True
[1, 2, [3, 4], [5, [6, 7]]]
[1, 2, [3, -1], [5, [6, 7]]]

从结果中可以看出,n 和 m 已不是同一个对象,对于某个元素的重新赋值不会影响原对象。但是,它们内部的元素全都是一样的,所以对一个可变类型元素的修改,则仍然会反应在原对象中。

1d7b83e932a87b549d579b748c1add12.png

(其实这里1、2也是指向同一个对象,但作为不可变对象来说,它们互不影响,直观上的感受就相当于是复制了一份,故简化如图上所示)

这种复制方法叫做浅拷贝shallow copy),又被人形象地称作“新瓶装旧酒”,虽然产生了新对象,但里面的内容还是来自同一份。

如果要彻底地产生一个和原对象完全独立的复制品,得使用深拷贝deep copy):

from copy import deepcopy
m = [1, 2, [3, 4], [5, [6, 7]]]
print('m:', id(m))
print([id(i) for i in m])
n = deepcopy(m)
print('n:', id(n))
print([id(i) for i in n])
print(n is m)
print(n[0] is m[0])
print(n[2] is m[2])
n[0] = -1
print(m)
n[2][1] = -1
print(m)

输出

m: 4389131400
[4381886832, 4381886864, 4389131272, 4389131528]
n: 4389131208
[4381886832, 4381886864, 4389131656, 4389145736]
False
True
False
[1, 2, [3, 4], [5, [6, 7]]]
[1, 2, [3, 4], [5, [6, 7]]]

此时,对新对象中元素做任何改动都不会影响原对象。新对象中的子列表,无论有多少层,都是新的对象,有不同的地址。

d8d61eebfafebe630885b1b2d0420807.png

按照前面的比喻,深拷贝就是“新瓶装新酒”。

你可能会注意到一个细节:n 中的前两个元素的地址仍然和 m 中一样。这是由于它们是不可变对象,不存在被修改的可能,所以拷贝和赋值是一样的

于是,深拷贝也可以理解为,不仅是对象自身的拷贝,而且对于对象中的每一个子元素,也都进行同样的拷贝操作。这是一种递归的思想。

不过额外要说提醒一下的是,深拷贝的实现过程并不是完全的递归,否则如果对象的某级子元素是它自身的话,这个过程就死循环了。实际上,如果遇到已经处理过的对象,就会直接使用其引用,而不再重复处理。听上去有点难懂是不是?想想这个例子大概就会理解了:

from copy import deepcopy
m = [1, 2]
m.append(m)
print(m, id(m), id(m[2]))
n = deepcopy(m)
print(n, id(n), id(n[2]))

输出

[1, 2, [...]] 4479589576 4479589576
[1, 2, [...]] 4479575048 4479575048

最后,还是给各位留个思考题:

from copy import deepcopy
a = [3, 4]
m = [1, 2, a, [5, a]]
n = deepcopy(m)
n[3][1][0] = -1
print(n)

欢迎在评论区留下你的答案。


说个事儿:Crossin的新书《码上行动:零基础学会Python编程(ChatGPT版)》马上就要正式跟大家见面了。

280b2f9eeb91a309f56fc1854d61efa9.jpeg

本书力求做到浅显易懂,让完全没有编程经验的零基础“小白”也能学会Python。全书共 17 章,涵盖了Python的环境搭建、基础语法、常见数据类型、实用模块、正则表达式、面向对象编程、多任务编程等方面的知识点。另外还提供了爬虫、GUI、游戏三个实战项目。书中还创新地使用 ChatGPT 作为编程学习的辅助,带领读者探索AI时代下学习编程的新模式。

公众号的读者朋友购买后可在后台联系我,加入读者交流群,我会解答你在阅读本书时的一切疑问。

目前当当正在5折预售中,扫描下方图片二维码可提前预订。

615c4af1e1ec56ec177354cbe999f8c0.jpeg

感谢转发点赞的各位~


_往期文章推荐_

Tkinter:Python的门面担当


如需了解付费精品课程教学答疑服务

请在Crossin的编程教室内回复: 666

672e17747c49061ab70d19a0f94eb59c.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/520329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Marking

Marking 题目描述 输入输出 #include<iostream> #include<algorithm> using namespace std; typedef long long ll; ll gcd(ll a,ll b) {return b0?a:gcd(b,a%b); } int main() {ll t;cin>>t;ll n,d,k;while(t--){cin>>n>>d>>k;k--;if(g…

数据结构学习分享之树的介绍

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:数据结构学习分享⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你了解更多数据结构的知识   &#x1f51d;&#x1f51d; 数据结构第六课 1. 前言&a…

5.13下周黄金最新行情走势分析及多空交易策略

近期有哪些消息面影响黄金走势&#xff1f;下周黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周五(5月12日)美市尾盘&#xff0c;现货黄金收报2010.23美元/盎司&#xff0c;下跌4.57美元或0.23%&#xff0c;日内最高触及2022.59美元/盎司&#xff0c;最低触及…

IDEA 2022.3版本安装

安装前的准备 (免费使用方式私信我&#xff01;&#xff01;) • 64 位 Microsoft Windows 11、10、8 • 最低 2 GB 可用 RAM&#xff0c;推荐 8 GB 系统总 RAM • 2.5 GB 硬盘空间&#xff0c;推荐 SSD • 最低屏幕分辨率 1024x768 从安装上来看&#xff0c;IntelliJ IDEA 对硬…

asp.net+C#-高校社团管理网站

1、前台&#xff08;主要提供给学生使用&#xff09;&#xff1a;&#xff08;1&#xff09;注册&#xff0c;学生可以注册&#xff08;2&#xff09;登录&#xff0c;用户可以登录系统&#xff08;3&#xff09;查看社团活动&#xff0c;可以在页面查看社团的详细活动内容&…

Python基础入门编程代码练习(七)

一、读取json文件 创建一个json格式的文件,包含以下信息&#xff08;注意&#xff0c;地址是一个json对象&#xff0c;包含国家和城市&#xff09;&#xff0c;请使用Python中读取json文件的方法获取并打印json对象中的姓名、年龄以及城市信息 我叫小明,我今年 18 岁,性别男,…

如何批量查询自己的CSDN博客质量分

批量查询自己的CSDN博客质量分 一&#xff1a;故事背景二&#xff1a;具体实现2.1 csdn 接口分析2.2 apiPost调用接口2.2 RestTemplate进行接口调用2.2.1 需要引入的Maven依赖2.2.2 调用2.2.2 两个接口2.2.3 导出Excel到自己的电脑2.2.4 效果 三&#xff1a;总结提升 一&#x…

POWERLINK协议源码(最新)在stm32单片机上的移植指南

最近着了powerlink的道&#xff0c;连续几晚十二点前没睡过觉。不得不说兴趣这东西劲太大了&#xff0c;让人睡不着。喜欢上研究POWERLINK&#xff0c;最新版的源码结构挺清晰的&#xff0c;移植并测试了嵌入式linux作为从站和电脑主站之间的通信&#xff0c;挺有趣的。接下来想…

idea注释配置

走过路过不要错过&#xff01; 如果你也想要这样的注释模板&#xff0c;前接着往下看&#xff01; 注释模板有两种方式 1、新建类的时候自动添加注释 按照上图中的提示&#xff0c;找到settings中的File Code Templates选择右边Files选项卡中&#xff0c;选择class在右侧输入…

python中最全面的统计分布工具

文章目录 正态分布属性和方法测试 正态分布 scipy为诸多统计分布函数设计了类&#xff0c;这些类封装了几乎相同的方法&#xff0c;下面先以正态分布为示例&#xff0c;预览一下强大的统计功能。 import numpy as np from scipy.stats import norm import matplotlib.pyplot …

K_A38_001 基于STM32等单片机驱动DS1302时钟模块 OLED0.96显示

K_A38_001 基于STM32等单片机驱动DS1302时钟模块 OLED0.96显示 所有资源导航一、资源说明二、基本参数参数引脚说明 三、驱动说明时序对应程序: 四、部分代码说明1、接线引脚定义1.1、STC89C52RCDS1302时钟模块1.2、STM32F103C8T6DS1302时钟模块 五、基础知识学习与相关资料下载…

Python入门(六)if语句(一)

if语句&#xff08;一&#xff09; 1.概述2.一个简单的实例3. 条件测试3.1 检查是否相等3.2 数值比较3.3 检查多个条件3.4 布尔表达式 作者&#xff1a;xiou 1.概述 编程时经常需要检查一系列条件&#xff0c;并据此决定采取什么措施。在Python中&#xff0c;if语句让你能够检…

三分钟阿里云服务器u1通用算力型性能、使用限制及费用说明

阿里云服务器u1是通用算力型云服务器&#xff0c;CPU采用2.5 GHz主频的Intel(R) Xeon(R) Platinum处理器&#xff0c;通用算力型u1云服务器不适用于游戏和高频交易等需要极致性能的应用场景及对业务性能一致性有强诉求的应用场景(比如业务HA场景主备机需要性能一致)&#xff0c…

ChatGPT 快速阅读论文:3 个步骤让你轻松阅读论文文档

ChatGPT 快速阅读论文:3 个步骤让你轻松阅读论文文档 一、引言二、什么是 ChatDOC三、如何使用 ChatDOC3.1 主要功能3.2 表格/文本分析3.3 人工智能对话最后附使用地址:本教程收集于:AIGC从入门到精通教程 一、引言 你是否曾经因为阅读论文而感到困难和无从下手?ChatDOC 是…

分布式 05.redis在springboot中的使用和两个注解@Configuration和@Bean

01.在服务器中配置好redis服务器后&#xff0c;在sts中使用redis插件。 02.先是导入jar包&#xff1a; <!--spring整合redis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> &l…

MySQL--复合查询--内外链接--0422

注&#xff1a;为了方便查看 mysql语句会有突然的换行&#xff0c;书写时请勿模仿。 目录 1.单表查询回顾 显示工资最高的员工的名字和工作岗位 显示工资高于平均工资的员工信息 2.多表查询 比如需要查询雇员名字和所在部门编号及部门名字。 显示部门号为10的部门名&…

win10+使用cmake编译最新的ceres-solver库

参考文献 点击这里访问 感谢大佬&#xff01; 说明 我这个只编译了release版本&#xff0c;如果还需要debug版本&#xff0c;cmake编译和vs编译都需要另外设置一些东西&#xff0c;我没有弄。 文章目录 参考文献说明1. 需要使用的库2. 需要的工具3. 准备工作3.1 建一个文件…

Neovim-配置教程

环境&#xff1a;Ubuntu 20.04 宿主机&#xff1a;windows &#xff08;windows terminal&#xff09;WSL2 NVIM&#xff1a;v0.10.0-dev 配置Neovim 需要保证流畅的github环境&#xff08;以便于快速拉取插件&#xff09;&#xff0c;可以使用代理或是配置Github SSH key 本文…

【OpenCV-Python】——Haar人脸检测深度学习人脸检测EigenFaces/FisherFaces/LBPH人脸识别

目录 前言&#xff1a; 1、人脸检测 1.1 基于Haar的人脸检测 1.2 基于深度学习的人脸检测 2、人脸识别 2.1 EigenFaces人脸识别 2.2 FisherFaces人脸识别 2.3 LBPH人脸识别 总结&#xff1a; 前言&#xff1a; 人脸检测是人脸定位的过程&#xff1b;人脸识别是在人脸…

macOS 下载汇总(系统、应用和教程) 2023 持续更新中

macOS Ventura 13, macOS Monterey 12, macOS Big Sur 11, macOS Catalina 10.15, macOS Mojave 10.14, macOS High Sierra 10.13, macOS Sierra 10.12 请访问原文链接&#xff1a;https://sysin.org/blog/macOS/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。…