引用与浅拷贝与深拷贝

一、随笔

从没想过浅拷贝和引用是不是一回事,以前没仔细研究过,有些模糊,今天才发现是对新手和门外汉(我特么就是个门外汉)的低砍腿(切踢、下路扫腿、扫堂腿,低扫腿,扫下盘)。姜维打击!!!

二、程序场景

>>> results = []
>>> path = [1,2,3]
>>> results.append(path)
>>> results
[[1, 2, 3]]
>>> results = []
>>> path = [1,2,3]
>>> results.apppend(path[:])
>>> results
[[1, 2, 3]]

上面这两个,目前答案是一样的,但是后续path变化,效果就不同了

>>> results = []

>>> path = [1,2,3]
>>> results.append(path)
>>> results
[[1, 2, 3]]
>>> results.append(path[:])
>>> results
[[1, 2, 3], [1, 2, 3]]
>>> path.append(4)
>>> results
[[1, 2, 3, 4], [1, 2, 3]]
  • 特别注意:path增加到了4,但是resutls中只有第一个元素变化了。

在Python中,results.append(path)results.append(path[:])的主要区别在于它们如何处理列表的引用。

  1. results.append(path):这种方式将path列表的引用添加到results列表中。这意味着,如果之后path列表的内容发生变化,那么results中的相应元素也会反映出这些变化,因为它们引用的是同一个列表对象。
  2. results.append(path[:])这种方式首先创建path列表的一个浅拷贝然后将这个拷贝的引用添加到results列表中。因此,即使之后path列表的内容发生变化,results中的相应元素也不会受到影响,因为它们引用的是path列表的旧拷贝。
    简单来说,使用results.append(path[:])可以确保results中的每个元素都是独立的,不会受到原始path列表后续变化的影响。而results.append(path)则会导致results中的元素随着path的变化而变化。

三、列表展示

下面是一个详细的列表,展示了在Python中,对于不同数据类型(数字、字符串、元组、字典、列表、集合、对象)使用引用、浅拷贝和深拷贝时的行为详情:

数据类型引用 (Copy by Reference)浅拷贝 (Shallow Copy)深拷贝 (Deep Copy)
数字 (int, float)创建一个指向同一内存位置的引用。修改原始值不会影响引用。创建一个新值,与原始值相同。创建一个新值,与原始值相同。
字符串 (str)创建一个指向同一内存位置的引用。由于字符串是不可变的,修改原始字符串不会影响引用。创建一个新的字符串,与原始字符串相同。创建一个新的字符串,与原始字符串相同。
元组 (tuple)创建一个指向同一内存位置的引用。由于元组是不可变的,修改原始元组不会影响引用。创建一个新的元组,与原始元组相同。创建一个新的元组,与原始元组相同。
字典 (dict)创建一个指向同一字典的引用。修改原始字典会影响引用。创建一个新的字典,但保留原始字典中元素的引用。创建一个新的字典,并递归复制所有元素。
列表 (list)创建一个指向同一列表的引用。修改原始列表会影响引用。创建一个新的列表,但保留原始列表中元素的引用。创建一个新的列表,并递归复制所有元素。
集合 (set)创建一个指向同一集合的引用。修改原始集合会影响引用。创建一个新的集合,但保留原始集合中元素的引用。创建一个新的集合,并递归复制所有元素。
对象 (object)创建一个指向同一对象的引用。修改原始对象会影响引用。创建一个新的对象,但保留对象内部属性的引用。创建一个新的对象,并递归复制所有属性。

注意事项:

  • 对于不可变数据类型(如数字、字符串、元组),引用和浅拷贝的效果是相同的,因为它们的值不能被改变
  • 深拷贝会递归复制所有可变数据类型,以确保原始对象和复制对象之间完全独立。
  • 对于自定义对象,深拷贝会尝试复制对象的所有属性,这可能涉及到对象的__deepcopy__方法。
  • 浅拷贝和深拷贝对于嵌套数据结构(如列表中的列表)的影响最为明显,因为浅拷贝只复制最外层的容器,而深拷贝会复制所有层级。
    在Python中,可以使用copy模块来进行浅拷贝和深拷贝:
import copy
original_list = [[1, 2, 3], [4, 5, 6]]
# 浅拷贝
shallow_copied_list = copy.copy(original_list)
# 深拷贝
deep_copied_list = copy.deepcopy(original_list)

在这个例子中,如果你修改了original_list中的任何内部列表,这些修改会在shallow_copied_list中体现出来,但不会在deep_copied_list中体现。

四、扩展

在Python中,is==操作符用于比较对象,但它们的目的和结果不同。is用于比较两个对象的身份(即它们是否是同一个对象),而==用于比较两个对象的值(即它们是否具有相同的值)。对于引用、浅拷贝和深拷贝,这两个操作符的作用会有所不同。下面是一个列表,展示了对于不同数据类型(包括None)使用is==时的行为详情:

数据类型/操作引用 (Copy by Reference)浅拷贝 (Shallow Copy)深拷贝 (Deep Copy)
数字 (int, float)is:True(对于不可变类型,引用和浅拷贝通常指向同一个对象)
==:True
is:False(创建了新的对象)
==:True
is:False(创建了新的对象)
==:True
字符串 (str)is:True(对于不可变类型,引用和浅拷贝通常指向同一个对象)
==:True
is:False(创建了新的对象)
==:True
is:False(创建了新的对象)
==:True
元组 (tuple)is:True(对于不可变类型,引用和浅拷贝通常指向同一个对象)
==:True
is:False(创建了新的对象)
==:True
is:False(创建了新的对象)
==:True
字典 (dict)is:True(指向同一个对象)
==:True
is:False(创建了新的字典,但内部元素是引用)
==:True
is:False(创建了新的字典,所有元素都被复制)
==:True
列表 (list)is:True(指向同一个对象)
==:True
is:False(创建了新的列表,但内部元素是引用)
==:True
is:False(创建了新的列表,所有元素都被复制)
==:True
集合 (set)is:True(指向同一个对象)
==:True
is:False(创建了新的集合,但内部元素是引用)
==:True
is:False(创建了新的集合,所有元素都被复制)
==:True
对象 (object)is:True(指向同一个对象)
==:取决于对象的__eq__方法
is:False(创建了新的对象,但内部属性是引用)
==:取决于对象的__eq__方法
is:False(创建了新的对象,所有属性都被复制)
==:取决于对象的__eq__方法
Noneis:True(只有None是自身的引用)
==:True
is:False(None是不可变的,但浅拷贝会创建新的对象)
==:True
is:False(None是不可变的,但深拷贝会创建新的对象)
==:True
注意事项:
  • 对于不可变数据类型(如数字、字符串、元组),is==在引用和浅拷贝的情况下通常都返回True,因为Python可能会优化这些不可变类型的重复值,使它们共享相同的内存地址。
  • 对于可变数据类型(如字典、列表、集合),is在引用时返回True,在浅拷贝和深拷贝时返回False,因为这两种拷贝方式都会创建新的对象。
  • 对于自定义对象,==的行为取决于对象的__eq__方法。
  • None是一个特殊的单例对象,只有None是自身的引用,所以is None== None通常用于检查是否为None
    在Python中,is==的正确使用取决于你想要比较的是对象的身份还是值。通常,当你想要比较两个对象是否完全相同(即它们是否是同一个对象)时,你应该使用is。当你想要比较两个对象的值是否相等时,你应该使用==

在Python中,is===用于不同的目的,它们在判断引用、浅拷贝和深拷贝时的作用也不同。下面是一个列表,展示了这些操作符对None和其他所有对象数据类型的作用:

操作符描述对None的作用对其他对象数据类型的作用
is比较两个对象的身份(即是否为同一个对象的实例)。用于检查是否为None,例如obj is None用于检查两个变量是否引用同一个对象。
==比较两个对象的值是否相等。不用于检查None,因为None == None是True,但这不提供额外信息。用于检查两个对象的值是否相等。
=赋值操作符,用于将右侧的值赋给左侧的变量。用于将None赋值给变量,例如obj = None用于将对象的值或引用赋值给变量。

对于引用、浅拷贝和深拷贝,这些操作符的作用如下:

操作符引用浅拷贝深拷贝
is用于检查两个变量是否引用同一个对象。is用于检查浅拷贝后的对象是否与原始对象相同(对于不可变类型,它们通常是相同的;对于可变类型,它们是不同的)。is用于检查深拷贝后的对象是否与原始对象相同(它们通常是不同的)。
==用于检查两个引用的对象是否具有相同的值。==用于检查浅拷贝后的对象是否具有与原始对象相同的值(它们通常是相同的,但如果原始对象包含可变对象,则可能会不同)。==用于检查深拷贝后的对象是否具有与原始对象相同的值(它们通常是相同的)。
=用于创建引用。=用于执行浅拷贝操作,例如copied_obj = copy.copy(original_obj)=用于执行深拷贝操作,例如copied_obj = copy.deepcopy(original_obj)

注意事项:

  • is用于比较两个对象的身份,即它们是否在内存中占据相同的位置。
  • ==用于比较两个对象的值,即它们的内容是否相等。
  • =用于赋值,创建新的引用或者执行浅拷贝或深拷贝操作
  • 对于不可变数据类型(如数字、字符串、元组),浅拷贝和深拷贝的效果通常是相同的,因为它们的值不能被改变。
  • 对于可变数据类型(如列表、字典、集合),浅拷贝和深拷贝会有明显的不同,因为浅拷贝只复制最外层的容器,而深拷贝会递归复制所有层级。
    在Python中,None是一个单例对象,用于表示缺少值或默认情况。通常使用is来检查一个变量是否为None因为is None== None更加明确和快速

五、总结

注意!注意!注意!虽然对于不可变数据类型(注意和可迭代区分,要有记忆隔离鸿沟)引用和浅拷贝效果相同但是浅拷贝和引用不是一回事

引用是直接指向同一个对象的指针,而浅拷贝是创建一个新对象,但它内部仍然包含对原始对象内部元素的引用。

在Python中,=操作符默认是引用。当你使用=将一个对象赋值给另一个变量时,你实际上是在创建一个新的引用,而不是复制对象本身。

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值