python函数方法里面用浅复制深复制_图解 Python 浅拷贝与深拷贝

Python 中的赋值语句不会创建对象的拷贝,仅仅只是将名称绑定至一个对象。对于不可变对象,通常没什么差别,但是处理可变对象或可变对象的集合时,你可能需要创建这些对象的 “真实拷贝”,也就是在修改创建的拷贝时不改变原始的对象。

本文将以图文方式介绍 Python 中复制或“克隆”对象的操作。

首先介绍一下 Python 中浅拷贝与深拷贝的区别:

浅拷贝:浅拷贝意味着构造一个新的集合对象,然后用原始对象中找到的子对象的引用来填充它。从本质上讲,浅层的复制只有一层的深度。复制过程不会递归,因此不会创建子对象本身的副本。

深拷贝:深拷贝使复制过程递归。这意味着首先构造一个新的集合对象,然后递归地用在原始对象中找到的子对象的副本填充它。以这种方式复制一个对象,遍历整个对象树,以创建原始对象及其所有子对象的完全独立的克隆。

赋值与引用

在开始浅拷贝与深拷贝前,我们先来看一下 Python 中的赋值与引用。

lst = [1, 2, 3]

new_list = lst

从字面上看,上述语句创建了变量 lst 和 new_list,并且 lst 和 new_list 的赋值都为一个列表。但是,Python 的赋值语句并不会复制对象,而是会重新创建一个对象的引用。

可以看出,lst 和 new_list 都引用了同一个列表。

创建浅拷贝

不少教程里都会提到,如果你有一个列表,当你想要修改列表中的值但却不想影响原始对象时,可以使用 list 复制(浅拷贝)一个列表。

我们先来试一下:

lst = [1, 2, 3]

new_list = list(lst)

没错,lst 和 new_list 分别指向了不同的列表。当修改 lst 列表中的值时,并不会对 new_list 对象产生影响。

lst[0] = 'x'

print(lst)

print(new_list)

['x', 2, 3]

[1, 2, 3]

之所以说 list 语句是浅拷贝,是因为这种修改只对一层对象有效,当列表中有子对象时,对子对象的修改将影响原始对象和浅拷贝对象。

为了解释这一说法,让我们先创建一个嵌套列表,并使用 list 函数创建浅拷贝。

lst = [[1, 2, 3], [4, 5, 6]]

new_list = list(lst)

这里 new_list 是有着和 lst 一样内容的新的独立的对象。

可以看到 lst 和 new_list 分别指向了不同的对象。

对第一层 lst 的修改,将不会对 new_list 副本造成影响。

lst.append([7, 8, 9])

print(lst)

print(new_list)

[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

[[1, 2, 3], [4, 5, 6]]

但是,因为我们只创建了原始列表的一个浅拷贝,所以 new_list 仍然包含对 lst 中存储的原始子对象的引用。

也就是如上图所示,lst 和 new_list 的子列表都指向了相同的对象。

子对象没有被复制,它们只是在复制的列表中被再次引用。

因此,当你修改 lst 中的一个子对象时,这种修改也会反映到 new_list 中—— 这是因为两个列表共享相同的子对象。这种复制只是一个浅的,一个层级的复制:

lst[0][0] = 'x'

print(lst)

print(new_list)

[['x', 2, 3], [4, 5, 6], [7, 8, 9]]

[['x', 2, 3], [4, 5, 6]]

如果我们在第一步中创建了一个 lst 的深拷贝,那么两个对象就完全独立了。这是对象的浅拷贝和深拷贝之间的实际区别。

使用 Python 标准库中的 copy 模块可以创建深拷贝,这个模块为创建任意 Python 对象的浅拷贝和深拷贝提供了一个简单的接口。

创建深拷贝

这次我们使用 deepcopy() 函数创建一个对象的深拷贝:

import copy

lst = [[1, 2, 3], [4, 5, 6]]

new_list = copy.deepcopy(lst)

从图中可以看出 lst 和 new_list 中的子对象指向了不同的对象,如果对 lst 的子对象进行修改,将不会影响 new_list。

这一次,原始对象和复制对象都是完全独立的。如前面所说,递归克隆了 lst,包括它的所有子对象:

lst[0][0] = 'x'

print(lst)

print(new_list)

[['x', 2, 3], [4, 5, 6]]

[[1, 2, 3], [4, 5, 6]]

copy 模块中的 copy.copy() 函数也可以创建对象的浅拷贝。使用 copy.copy() 可以明确地表示创建浅拷贝。对于内置集合,简单地使用 list、dict 和 set 等工厂函数来创建浅拷贝是更加 Pythonic 的。

复制任意 Python 对象

copy.copy() 和 copy.deepcopy() 函数可用于复制任意对象。以前面的列表复制示例为基础。让我们从定义一个简单的 2D 点类开始:

class Point:

def __init__(self, x, y):

self.x = x

self.y = y

def __repr__(self):

return f'Point({self.x!r}, {self.y!r})'

__repr__() 函数使我们可以轻松地在 Python 解释器中检查从这个类创建的对象。

接下来,我们将创建一个 Point 实例,然后使用 copy 模块复制(浅拷贝)它:

a = Point(23, 42)

b = copy.copy(a)

print(a is b)

False

a 和 b 分别指向了不同的 Point 实例。因为我们的 Point 对象使用不可变类型(int)作为其坐标,所以在这种情况下,浅拷贝和深拷贝没有区别。但我马上会展开这个例子。

接下来定义另一个类来表示 2D 矩形。矩形将使用 Point 对象来表示它们的坐标:

class Rectangle:

def __init__(self, topleft, bottomright):

self.topleft = topleft

self.bottomright = bottomright

def _repr__(self):

return (f'Rectangle({self.topleft!r}, {self.bottomright!r})')

# 创建一个 Rectangle 实例的浅拷贝

rect = Rectangle(Point(0, 1), Point(5, 6))

shallow_rect = copy.copy(rect)

print(rect)

print(shallow_rect)

print(rect is shallow_rect)

Rectangle(Point(0, 1), Point(5, 6))

Rectangle(Point(0, 1), Point(5, 6))

False

跟前面 list 的例子一样,rect 和 shallow_rect 的子对象都有相同的引用。在对象层级中修改一个对象,将看到这个变化也反映在浅拷贝的副本中:

rect.topleft.x = 999

print(rect)

print(shallow_rect)

Rectangle(Point(999, 1), Point(5, 6))

Rectangle(Point(999, 1), Point(5, 6))

接下来创建 Rectangle 的深拷贝并对其进行修改:

deep_rect = copy.deepcopy(rect)

deep_rect.topleft.x = 222

print(rect)

print(shallow_rect)

print(deep_rect)

Rectangle(Point(999, 1), Point(5, 6))

Rectangle(Point(999, 1), Point(5, 6))

Rectangle(Point(222, 1), Point(5, 6))

可以看出,深拷贝完全独立于原始对象和浅拷贝对象。

参阅 copy 模块文档 可以对复制进行进一步的研究。例如,对象可以通过定义特殊的方法 __copy__() 和 __deepcopy__() 来控制如何复制它们。

谨记三件事

创建对象的浅拷贝不会克隆子对象。因此,拷贝不会完全独立于原始对象。

一个对象的深拷贝会递归地克隆子对象。克隆对象完全独立于原始对象,但是创建深拷贝速度较慢。

可以使用 copy 模块复制任意对象(包括自定义类)。

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

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

相关文章

32位Windows系统未分页内存限制导致的VPS的容量问题

问题 32位Windows系统未分页内存限制导致的VPS的容量问题 解决方案 在32位的Windows系统上,制约VPS的数量的主要参数是“未分页内存” 打开Windows自己的任务管理器,选择性能标签,查看“核心内存”,查看“未分页” 在32位的windo…

AcWing之重建二叉树

题目 输入一棵二叉树前序遍历和中序遍历的结果,请重建该二叉树。 注意:二叉树中每个节点的值都互不相同;输入的前序遍历和中序遍历一定合法;样例 给定: 前序遍历是:[3, 9, 20, 15, 7] 中序遍历是:[9, 3, …

奇异值分解(Singular Value Decomposition,SVD)

文章目录1. 奇异值分解的定义与性质1.1 定义1.2 两种形式1.2.1 紧奇异值分解1.2.2 截断奇异值分解1.3 几何解释1.4 主要性质2. 奇异值分解与矩阵近似2.1 弗罗贝尼乌斯范数2.2 矩阵的最优近似2.3 矩阵的外积展开式3. 奇异值分解Python计算4. SVD应用一种矩阵因子分解方法矩阵的奇…

项目经理有必要学python吗_项目经理到底要不要懂技术

关于这个问题,我想开门见山地说一句,需要,而且非常需要。当然有同学就会说了,项目经理懂技术会被技术所束缚,无法跳出技术角度来看待项目整体。还有同学会说,现在是团队配合时代,各领域专精&…

贝叶斯算法

1. 贝叶斯由来 贝叶斯为了解决“逆概”问题提出的 2. 贝叶斯要解决的问题 正向概率 袋子里装着N个黑球和M个白球,伸手取摸球,摸到黑球和白球的概率有多大 逆向概率 袋子里前提不知道有黑白球的比例,而是闭着眼睛摸球统计后推测黑球和白球的…

LeetCode 374. 猜数字大小(二分查找)

1. 题目 我们正在玩一个猜数字游戏。 游戏规则如下: 我从 1 到 n 选择一个数字。你需要猜我选择了哪个数字。每次你猜错了,我会告诉你这个数字是大了还是小了。 你调用一个预先定义好的接口 guess(int num),它会返回 3 个可能的结果&#…

机器人编程与python语言的区别_一分钟看懂“机器人编程”和“少儿编程”的区别!...

随着编程学习全球化的趋势,国内编程学习热潮日盛,越来越多的家长开始让孩子接触学习编程。然而在挑选学习课程的过程中,机器人编程和少儿编程是最让家长头疼的问题之一。因为两者的名称都有“编程”二字,这就让很多家长产生一个错…

让窗体接受拖放, 并获取拖过来的文件信息 - 回复 海浪问 的问题

问题来源: http://www.cnblogs.com/del/archive/2009/01/20/1353117.html#1435746原理分析:这需要用到 ShellAPI 单元的两个函数: DragAcceptFiles、DragQueryFile;用 DragAcceptFiles(窗口句柄, True); 以让窗口能够接受拖放;然后就等待 WM_DROPFILES 消息, 并用 DragQueryFil…

LeetCode之最大回文串--动态规划

1. 题目 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 示例 1:输入: "babad"输出: "bab" 注意: "aba" 也是一个有效答案。 示例 2:输入: "cbbd"输出: "bb&q…

sort函数_MATLAB--数字图像处理 sort()函数

sort()用处对一维或二维矩阵进行排序用法sort(A):对一维或二维矩阵进行升序排序,并返回排序后的矩阵;当A为二维矩阵时,对矩阵的每一列分别进行升序排序(列优先)。sort(A,dim):对矩阵按指定的方向进行升序排序&#xff…

LeetCode 1248. 统计「优美子数组」(要复习)

文章目录1. 题目2. 解题2.1 记录奇数出现的pos2.2 前缀和1. 题目 给你一个整数数组 nums 和一个整数 k。 如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。 请返回这个数组中「优美子数组」的数目。 示例 1: 输入…

LeetCode 560. 和为K的子数组(前缀和差分)

1. 题目 给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。 示例 1 : 输入:nums [1,1,1], k 2 输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。 说明 : 数组的长度为 [1, 20,000]。 数组中元素的范围是 [-1000, 1000] ,且…

2月4日 星期三

昨天几点睡掉的? 估计猫是不会睡太早了 以后拍照不用担心猫牙了 想怎么笑就怎么笑 是不是 多给家里传传吧 估计很多人都想看到小猫呢 爸爸今天要回本溪了 下午2点车 我去送他 亲亲 转载于:https://www.cnblogs.com/loverain/archive/2009/02/04/1383549.html

LeetCode之最小路径和

1. 题目 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明:每次只能向下或者向右移动一步。 示例: 输入: [[1,3,1],[1,5,1],[4,2,1] ] 输出: 7 解释: 因为路径 1→3→1→1→1 的总和…

python ppt自动生成_如何自动化生成PPT缩略图?

搜到的代码,可能得自己改一下. 导出当前文件夹下所有 PowerPoint 演示文稿的第一张幻灯片 并以图形文件格式保存在当前文件夹下 变量声明和初始化Dim wShell, pptApp, fso, folder, file, slide, outFileSet wShell WScript.CreateObject("WScript.Shell") 获取当前…

LeetCode 862. 和至少为 K 的最短子数组(前缀和+deque单调栈)

1. 题目 返回 A 的最短的非空连续子数组的长度,该子数组的和至少为 K 。 如果没有和至少为 K 的非空子数组,返回 -1 。 示例 1: 输入:A [1], K 1 输出:1示例 2: 输入:A [1,2], K 4 输出&…

LeetCode之简单回文数

1. 题目 判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。示例 1: 输入: 121 输出: true示例 2: 输入: -121 输出: false 解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是…

python爬取全国真实地址_Python3爬虫全国地址信息

PHP方式写的一团糟所以就用python3重写了一遍,所以因为第二次写了,思路也更清晰了些。提醒:可能会有502的错误,所以做了异常以及数据库事务处理,暂时没有想到更好的优化方法,所以就先这样吧。待更懂python再…

Workflow WF Reference Links for 2009-02-13

Workflow Reference Links: 1. XPDL - The Silent Workhorse of BPM 我想如果要大致了解一下BPM的主要协议和规范,以及它们的关系,这是一篇很好的文章。在文章的后半部分,作者为XPDL进行了沉冤昭雪的工作。 2. Workflow Scalability a…

LeetCode 383. 赎金信

1. 题目 给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。 如果可以构成,返回 true ;否则返回 false。 (题目说明:为了不暴露赎金信字迹&a…