首页 > 编程语言 >Python对象的比较和拷贝

Python对象的比较和拷贝

时间:2022-12-28 17:02:46浏览次数:68  
标签:20 Python 对象 拷贝 copy True

Python对象的比较和拷贝

本文内容存在主观理解,详细请查阅官网文档

比较(== VS is)

==操作符是比较对象的值是否相等,而is比较的事对象的身份标识是否相等,即它们是否是同一个对象,是否指向同一个内存地址。例如:

>>>a = 6
>>>b = 6
>>>a == b
True
>>>id(a)
2545626147408
>>>id(b)
2545626147408
>>>a is b
True

对于整型数字来说,a is bTrue的结论只适用于\(-5-256\),因为出于对性能优化的考虑,Python内部会对\(-5-256\)的整型维持一个数组,起到缓存的作用,因此,我们每次创建一个\(-5-256\)范围内的整型数字时,Python会从这个数组返回对应的引用,而不会开辟一块新的内存空间。如果数字超过这个范围,Python就会重新开辟两块内存区域

>>>a = 257
>>>b = 257
>>>a is b
False
>>>id(a)
2076692650576
>>>id(b)
2076701657552

比较操作符is的效率要优于==,因为is运算符不能被重载,这样Python就不需要去寻找程序中其它地方是否重载了比较运算符。但是a==b相当于执行a.__eq__(b),而Python中大部分的数据类型都会重载__eq___这个函数,比如列表,这个函数会遍历列表中的元素,比较它们的顺序和值是否相等。

对于不可变的变量,如果使用==is比较过,那么结果是否是保持不变呢?

>>>a = (1, 2, [3, 4])
>>>b = (1, 2, [3, 4])
>>>a == b
True
>>>a[-1].append(5)
>>>a == b

元组类型是不可变的,但是元组可以嵌套可变数据类型,当我们修改了元组中的某个可变类型,元祖本身也就改变了,之前的比较结果就不再适用。

拷贝

浅拷贝

>>>a = [1, 2, 3]
>>>b = list(a)
>>>b
[1,2,3]
>>>a == b
True
>>>a is b
False

这里,ba的浅拷贝。对于可变的序列,可以通过切片实现浅拷贝,例如:

>>>a = [1, 2, 3]
>>>b = a[:]
>>>a == b
True
>>>a is b
False

或者使用copy.copy()函数,适用于任何的数据类型

>>>import copy
>>>a = [1, 2, 3]
>>>b = copy.copy(a)

对于元祖来说,使用tuple():不会创建一份浅拷贝,而是指向相同元祖的引用

>>>a = (1, 2, 3)
>>>b = tuple(a)
>>>a == b
True
>>>a is b
True

综上,浅拷贝是指重新分配一块内存,创建一个新的对象,里面的元素是原对象中子对象的引用(如果原对象中的元素可变,浅拷贝会带来一些副作用)

>>> a = [[1, 2], (3, 4)]
>>> b = list(a)
# 因为浅拷贝里的元素是对原对象元素的引用,因此b和a中的元素指向同一个列表和元组对象
>>> a.append(10)
# a中新增元素10,但因为b和a整体是不用的对象,所以对b没有影响
>>> a[0].append(20)
# 对a的第一个元素新增20,b和a中的第一个元素指向同一个列表,因此b的第一个元素也会增加20
>>> a
[[1, 2, 20], (3, 4), 10]
>>> b
[[1, 2, 20], (3, 4)]
>>> a[1] += (5, 6)
# 元组是不可变类型,这里是对a中的元组拼接让后**创建了一个新的元组**作为a的第二个元素,而b中没有引用新元组,所以b不发生改变
>>> a
[[1, 2, 20], (3, 4, 5, 6), 10]
>>> b
[[1, 2, 20], (3, 4)]

想要避免浅拷贝带来的副作用,可以使用深拷贝完整拷贝一个对象,即重新分配一块内存,创建一个新的对象,并将原对象中的元素以递归的方式通过创建新的子对象拷贝到新对象中,这样新对象与原对象之间没有任何关联。

Python中使用copy.deepcopy()实现对象的深度拷贝。

>>> import copy
>>> a = [[1, 2], (3, 4)]
>>> b = copy.deepcopy(a)
>>> a.append(10)
>>> a[0].append(20)
>>> a
[[1, 2, 20], (3, 4), 10]
>>> b
[[1, 2], (3, 4)]

此时,ab完全独立,无论a如何变化,b都不变。但是深度拷贝也存在一些问题,比如如果拷贝对象中存在指向自身的引用,就有可能陷入无限循环。

>>> import copy
>>> a = [1]
>>> a.append(a)
>>> a
[1, [...]]
>>> b = copy.deepcopy(a)
>>> b
[1, [...]]
>>> a == b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RecursionError: maximum recursion depth exceeded in comparison
# 进行比较操作符`==`的时候,会递归地遍历对象的所有值并逐一比较。而Python为了防止栈崩溃,到了限定的递归层数,解释器会报错。

在这个例子中,并没有出现栈溢出的情况,因为deepcopy会维护一个字典,记录已经拷贝的对象,如果字典中已经存储了要拷贝的对象,就直接从字典中返回。

(参考https://github.com/python/cpython/blob/main/Lib/copy.py)

标签:20,Python,对象,拷贝,copy,True
From: https://www.cnblogs.com/euler0525/p/17010512.html

相关文章

  • tensorflow_probability.python.bijectors的一些使用
      网上见到一个TensorFlow的代码,没见过这个形式的,是概率编程的代码:#coding=utf-8#Copyright2020TheTF-AgentsAuthors.##LicensedundertheApacheLicens......
  • python中global 和 nonlocal 的作用域
    python引用变量的顺序: 当前作用域局部变量->外层作用域变量->当前模块中的全局变量->python内置变量。一globalglobal关键字用来在函数或其他局部作用域中使用全局变量。......
  • 【leetcode】3: 无重复字串的最长子串(python)
    给定一个字符串s,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1:输入:s="abcabcbb"输出:3解释:因为无重复字符的最长子串是"abc",所以其长度为3......
  • [oeasy]python0033_回车_carriage_return_figlet_字体变大
    回到开头回忆上次内容进程前后台切换<kbd>ctrl</kbd>+<kbd>z</kbd>把当前进程切换到后台并暂停​​jobs​​查看所有作业用​​fg​​可以把后台进程再切回前台​​......
  • python logging配置
    python中,logging由logger,handler,filter,formater四个部分组成。logger是提供我们记录日志的方法;handler是让我们选择日志的输出地方,如:控制台,文件,邮件发送等,一个logger添加......
  • python中的mysql操作教程及实例
    一.数据库在自动化测试中的应用存测试数据有的时候大批量的数据,我们需要存到数据库中,在测试的时候才能用到,测试的时候就从数据库中读取出来。这点是非常重要的!存测试结......
  • 浅拷贝和深拷贝的区别
    1、Python中对象的赋值都是进行对象引用(内存地址)传递2、使用copy.copy(),可以进行对象的浅拷贝,它复制了对象,但对于对象中的元素,依然使用原始的引用3、如果需要复制一个容......
  • 数值计算:前向和反向自动微分(Python实现)
    1自动微分我们在《数值分析》课程中已经学过许多经典的数值微分方法。许多经典的数值微分算法非常快,因为它们只需要计算差商。然而,他们的主要缺点在于他们是数值的,这意味......
  • python中的集合推导式
    集合推导式可用来去重需求:将列表list1=[2,2,2,3,4,4,4]中的偶数进行筛选,并且去重list1=[2,2,2,3,4,4,4]set1={iforiinlist1ifi%2==0}print(set......
  • python中的列表推导式
    1.单列表,单条件求1-20之间的偶数list1=[]foriinrange(1,21):ifi%2==0:list1.append(i)print(list1)列表推导式list2=[iforiinrange(1,21)if......