之前的文章我们简单介绍了一下 Python 中异常处理, 本篇文章我们来看一下 Python 中 is 和 == 的区别及深拷贝和浅拷贝.
我们先来看一下在 Python 中的双等号 == .
== 是比较两个对象的内容是否相等, 即两个对象的 "值" 是否相等, 不管两者在内存中的引用地址是否一样.
is 比较的是两个实例对象是不是完全相同, 它们是不是同一个对象, 占用的内存地址是否相同. 即 is 比较两个条件: 1. 内容相同. 2. 内存中地址相同
- a = 10000
- b = 10000
- print(a == b) # True
- print(a is b) # True
- print(id(a)) # 4461408208
- print(id(b)) # 4461408208
- a = "hello world"
- b = "hello world"
- print(a == b) # True
- print(a is b) # True
- print(id(a)) # 4461408208
- print(id(b)) # 4461408208
- a = [11,22,33]
- b = [11,22,33]
- print(a == b) # True
- print(a is b) # False
- print(id(a)) # 4409720712
- print(id(b)) # 4409720776
在上面的代码中, 我们分别定义了 a 和 b 两个变量, 通过输出结果可以发现当变量为字符串或数字时, is 和 == 的输出结果是一样的, 当为列表时 is 和 == 结果不一样, 通过打印两个变量的 id 值可以看出两个 id 值不一样, 这是由于当我们创建列表 a 和 b 时, 是分别开辟了两块内存来分别存储这两个变量, 从表象上来看结果是一样的, 但两个变量所指向的内存地址不一样, 我们将上面的代码改为如下:
- a = [11,22,33]
- b = a
- print(a == b) # True
- print(a is b) # True
- print(id(a)) # 4535062408
- print(id(b)) # 4535062408
在上面的代码中, 我们并没有直接给变量 b 赋值, 而是让 b=a, 这样的话 b 和 a 就指向了同一块内存, 所以 a is b 就为 True 了.
上面的代码我是在 PyCharm 编辑器中实现的, 但是在终端命令行实现的话结果却是不一样的, 如下:
- >>> a = 10000
- >>> b = 10000
- >>> a == b
- True
- >>> a is b
- False
- >>> id(a)
- 4360555120
- >>> id(b)
- 4360555216
当我们将 a 和 b 的值变小时, 如下:
- >>> a = 100
- >>> b = 100
- >>> a == b
- True
- >>> a is b
- True
- >>> id(a)
- 4357367984
- >>> id(b)
- 4357367984
造成上面的原因是因为 python 对小整数在内存中直接创建了一份, 不会回收, 所有创建的小整数变量直接从对象池中引用他即可. 但是注意 Python 仅仅对比较小的整数对象进行缓存 (范围为范围[-5, 256]) 缓存起来, 而并非是所有整数对象. 也就说只有在这个 [-5,256] 范围内创建的变量值使用 is 比较时候才会成立.
在 PyCharm 中, 当值超过 256 时 is 和 == 的输出结果仍是一样, 这是因为解释器也做了一部分优化, 对于数字和字符串这类变量都进行了缓存.
我们再来看一下在终端命令行中当变量为字符串时:
- >>> a = "hello world"
- >>> b = "hello world"
- >>> a == b
- True
- >>> a is b
- False
- >>> id(a)
- 4359747248
- >>> id(b)
- 4361247408
- >>> a = "hello"
- >>> b = "hello"
- >>> a == b
- True
- >>> a is b
- True
- >>> id(a)
- 4361199040
- >>> id(b)
- 4361199040
通过输出结果可以看出, 在命令行中当变量为简单字符串时输出结果一致, 否则输出结果不一致, 这是由于 Python 对简单字符串对象也进行了缓存, 这样做的意义是可以优化代码的运行速度, 减少内存消耗.
在上面的代码中, 我们让 a=b 其实是一种浅拷贝的过程, 他们指向的是同一块内存, 当我们改变其中一个变量时, 另一个也会变, 如下:
- a = [11,22,33]
- b = a
- a[0] = 123
- print(a) # [123, 22, 33]
- print(b) # [123, 22, 33]
在 Python 中还有深拷贝, 它是重新开辟一块区域用来存储变量的, 如下:
- import copy
- a = [11, 22, 33]
- b = a
- c = [11, 22, 33]
- d = copy.deepcopy(a)
- print(a == b, a is b) # True True
- print(a == c, a is c) # True False
- print(a == d, a is d) # True False
- a[0] = 123
- print(a) # [123, 22, 33]
- print(b) # [123, 22, 33]
- print(c) # [11, 22, 33]
- print(d) # [11, 22, 33]
在上面的代码中我们引入了一个 copy 的模块, 引入模块这个之前的文章没有提到, 后续会单独说一下.
在 copy 模块中有一个 deepcopy() 的方法, 从上面的代码中可以看出 deepcopy() 的方法就相当于 c=[11,22,33] 这样重新开辟一块区域来存储变量, 当 a 变量改变时, 浅拷贝的内容 b 会随之改变, 但是深拷贝的内容 d 并不是指向 a 的内存地址, 所以不会改变.
在 copy 的模块中还有一个 copy() 方法, 它在某些方面看起来和 deepcopy() 方法是一样的, 其实不一样, 如下:
- import copy
- a = [11, 22, 33]
- b = copy.copy(a)
- c = copy.deepcopy(a)
- a[0] = 123
- print(a) # [123, 22, 33]
- print(b) # [11, 22, 33]
- print(c) # [11, 22, 33]
从上面的输出结果看, 当 a 变量修改之后, b 变量和 c 变量一样没有改变, 那 copy() 和 deepcopy() 到底有什么区别呢, 我们再看下面的代码:
- import copy
- a = [11, 22, [123,456]]
- b = copy.copy(a)
- c = copy.deepcopy(a)
- a[2][0] = 789
- print(a) # [11, 22, [789, 456]]
- print(b) # [11, 22, [789, 456]]
- print(c) # [11, 22, [123, 456]]
从上面的输出结果可以看出, b 变量随 a 变量的变化而变化了, 而 c 变量没有.
很显然这时 copy() 函数拷贝的值随着原对象的值修改了, 而 deepcopy() 的值没有随着原对象的值修改. 主要是因为 deepcopy() 会将复杂对象的每一层复制一个单独的个体出来对于 copy() 函数要慎用, 慎用.
来源: https://www.cnblogs.com/weijiutao/p/10733851.html