python 对象的比较和拷贝

一、 “==” 和 “is”

1.  比较操作符'=='表示比较对象间的值是否相等,而'is'表示比较对象的标识是否相等,即它们是否指向同一个内存地址,例如:

a = 10
b = 10
print(a == b)
print(id(a))
print(id(b))
print(a is b)

>> True
>> 140728663939008
>> 140728663939008
>> True

首先 Python 会为 10 这个值开辟一块内存,然后变量 a 和 b 同时指向这块内存区域,因此 a 和 b 的值相等,id 也相等。

但注意,对于整型数字来说,以上a is b为 True 的结论,只适用于 -5 到 256 范围内的小整数,这是因为python出于对性能优化的考虑,Python 内部会对 -5 到 256 的整型维持一个数组,起到一个缓存的作用,每次创建此范围的整型数字时,会从这个数组中返回相对应的引用,而非开辟一块新空间。

但是在pycharm执行时,不在[-5,256]范围的id值是一样的,是因为pycharm上运行Python出于对性能的考虑,不可变对象,相同的对象,就直接引用已经存在的对象

2、比较一个变量与一个单例(singleton)时,通常会使用'is'。一个典型的例子,就是检查一个变量是否为 None:


if a is None:
      ...

if a is not None:
      ...

 3、比较操作符'is'的速度效率,通常要优于'=='

由于'is'操作符不能被重载,Python 就无需寻找其他地方是否重载了比较操作符,并去调用。仅比较两个变量的 ID即可

'=='操作符,当执行a == b相当于是去执行a.__eq__(b),而 Python 大部分的数据类型都会去重载__eq__这个函数,其内部的处理通常会复杂一些。比如,对于列表,__eq__函数会去遍历列表中的元素,比较它们的顺序和值是否相等

二、浅拷贝和深度拷贝

1. 浅拷贝

方式一: 使用数据类型本身的构造器

l1 = [1, 2, 3]
l2 = list(l1)
print(l1 == l2)
print(l1 is l2)

>> True
>> False

方式二:通过切片操作符':'完成拷贝

l1 = [1, 2, 3]
l2 = l1[:]
print(l1 == l2)
print(l1 is l2)

>> True
>> False

方式三:通过函数 copy.copy(),适用于任何数据类型

import copy

l1 = [1, 2, 3]
l2 = copy.copy(l1)
print(l1 == l2)
print(l1 is l2)

>> True
>> False

以上三种方式对于可变数据类型(list, set,dic)都会创建一份浅拷贝。对于元组,使用 tuple() 或者切片操作符':'不会创建一份浅拷贝,相反,它会返回一个指向相同元组的引用

t1 = (1, 2, 3)
t2 = tuple(t1)
print(t1 == t2)
print(t1 is t2)

>> True
>> True

浅拷贝,是指重新分配一块内存,创建一个新的对象,里面的元素是原对象中子对象的引用。因此,如果原对象中的元素不可变,那倒无所谓;但如果元素可变,浅拷贝通常会带来一些副作用,如下:

l1 = [[1, 2], (30, 40)]
l2 = list(l1)
l1.append(100)
l1[0].append(3)
print(l1)  
print(l2)
>> [[1, 2, 3], (30, 40), 100]
>> [[1, 2, 3], (30, 40)]

l1[1] += (50, 60)
print(l1)
print(l2)
>> [[1, 2, 3], (30, 40, 50, 60), 100]
>> [[1, 2, 3], (30, 40)]

分析:

1)l1.append(100), l1 的列表新增元素 100。因为 l2 和 l1 作为整体是两个不同的对象,并不共享内存地址。操作过后 l2 不变,l1 会发生改变

2) l1[0].append(3), l1 中的第一个列表新增元素 3。因为 l2 是 l1 的浅拷贝,l2 中的第一个元素和 l1 中的第一个元素,共同指向同一个列表,因此 l2 中的第一个列表也会相对应的新增元素 3。操作后 l1 和 l2 都会改变

3) l1[1] += (50, 60),因为元组是不可变的,这里表示对 l1 中的第二个元组拼接,然后重新创建了一个新元组作为 l1 中的第二个元素,而 l2 中没有引用新元组,因此 l2 并不受影响。操作后 l2 不变,l1 发生改变

如果我们想避免这种副作用,完整地拷贝一个对象,你就得使用深度拷贝。

2. 深拷贝

深度拷贝,是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联

以 copy.deepcopy() 来实现对象的深度拷贝

import copy

l1 = [[1, 2], (30, 40)]
l2 = copy.deepcopy(l1)
l1.append(100)
l1[0].append(3)
print(l1)
print(l2)

>> [[1, 2, 3], (30, 40), 100]
>> [[1, 2], (30, 40)]

由上可见,无论 l1 如何变化,l2 都不变。因为此时的 l1 和 l2 完全独立,没有任何联系。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值