首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【python】不可变对象与可变对象 || 运算符 == 与 is 的区别 || 浅拷贝和深拷贝

【python】不可变对象与可变对象 || 运算符 == 与 is 的区别 || 浅拷贝和深拷贝

原创
作者头像
魏言
修改2025-02-16 08:07:01
修改2025-02-16 08:07:01
3770
举报

问题一:不可变对象与可变对象

含义:

不可变对象:是指创建后,内容不可修改的对象。我们只能通过重新创建这个对象的方式来实现修改它的内容。

可变对象:是指创建后,内容可修改的对象。修改内容后,这个对象本身还是之前那个对象。

常见不可变对象:如整数 int,浮点 float,字符串 str,元组 tuple 等。

常见可变对象:如列表 list,字典 dict,集合 set 等。

示例:

代码语言:javascript
复制
int_0 = 1
float_0 = 2.0
str_0 = 'hello'
list_0 = [1, 2, 3]
dict_0 = {'name': 'weiyan'}
print('修改前:')
print('整数:', id(int_0))
print('浮点:', id(float_0))
print('字符串:', id(str_0))
print('列表:', id(list_0))
print('字典:', id(dict_0))

int_0 = int_0 + 1
float_0 = float_0 + 2.0
str_0 = str_0.replace('h', 'H')
list_0[2] = 4
dict_0['name'] = 'WeiYan'
print('修改后:')
print('整数:', id(int_0))
print('浮点:', id(float_0))
print('字符串:', id(str_0))
print('列表:', id(list_0))
print('字典:', id(dict_0))

输出结果为:

代码语言:txt
复制
修改前:
整数: 2860989612272
浮点: 2860990653264
字符串: 2860994903728
列表: 2860994720256
字典: 2860994639872
修改后:
整数: 2860989612304
浮点: 2860990652720
字符串: 2860995006832
列表: 2860994720256
字典: 2860994639872

可见,修改内容后,整数、浮点数、字符串的内存地址都发生了改变,列表和字典的内存地址没变。

问题二:运算符 == 与 is 的区别

含义:

== 运算符:比较两个对象的值是否相等。即使两个不同对象,它们的值也可以相等。

is 运算符:比较两个对象是否相同。即它们是否指向内存的同一个位置。

用通俗的话来讲:

你家是三室一厅,你对象家也是三室一厅,就是 a = b 且 a is b,因为你俩的家指向的是同一套房子。

你家是三室一厅,某同事家也是三室一厅,就是 a = b 但是 a is not b,因为你俩的家指的不是同一套房子。

来看一些简单的示例:

代码语言:javascript
复制
list_a = ['a', 'b', 'c', ['d', 'e']]
list_b = list_a
print(list_a == list_b, list_a is list_b)
print(id(list_a))
print(id(list_b))

输出结果为:
2859388560832
2859388560832
True True

从上得知,我们查询两个列表的内存地址,结果是一样的,所以 a = b 而且 a is b

代码语言:javascript
复制
list_a = ['a', 'b', 'c', ['d', 'e']]
list_b = ['a', 'b', 'c', ['d', 'e']]
print(id(list_a))
print(id(list_b))
print(list_a == list_b, list_a is list_b)

输出结果为:
1397277490816
1397277487232
True False

此时,我们查询两个列表的内存地址,结果并不一样,所以虽然它们的内容相同 a = b 但是 a is not b

那么问题来了,两个对象的值是否相等很容易判断,但是怎么确定内存地址是否相同?每次都去 print(id(*)) 虽然能解决问题,但是毕竟麻烦。

这里就需要提到python的内存管理机制。首先看以下代码:

代码语言:javascript
复制
print('整数:')
num_a = 1
num_b = 1
print(num_a == num_b, num_a is num_b)
print(id(num_a), id(num_b))

print('字符串:')
str_a = 'hello'
str_b = 'hello'
print(str_a == str_b, str_a is str_b)
print(id(str_a), id(str_b))

print('元组:')
tuple_a = ('a', 'b', 'c')
tuple_b = ('a', 'b', 'c')
print(tuple_a == tuple_b, tuple_a is tuple_b)
print(id(tuple_a), id(tuple_b))

输出结果为:

代码语言:txt
复制
整数:
True True
2202396655856 2202396655856
字符串:
True True
2202397978480 2202397978480
元组:
True True
2202398310208 2202398310208

可见,此时指向的内存地址是一样的。

代码语言:javascript
复制
print('列表:')
list_a = ['a', 'b', 'c', [1000, 'e']]
list_b = ['a', 'b', 'c', [1000, 'e']]
print(id(list_a), id(list_b))
print(id(list_a[3]), id(list_b[3]))
print(id(list_a[3][0]), id(list_b[3][0]))

print('字典:')
dict_a = {'name': 'WeiYan', 'city': ['深圳', '景德镇']}
dict_b = {'name': 'WeiYan', 'city': ['深圳', '景德镇']}
print(id(dict_a), id(dict_b))
print(id(dict_a['city']), id(dict_b['city']))
print(id(dict_a['city'][0]), id(dict_b['city'][0]))

输出结果为:

代码语言:txt
复制
列表:
2456080737536 2456080694528
2456080740864 2456081027072
2456080409136 2456080409136
字典:
2456080660480 2456080660736
2456081026816 2456081027712
2456080802672 2456080802672

可见,列表和字典顶层内容的内存地址是不一样的,而内部嵌套的整数和字符串的内存地址却是重复的。

总结:

1:对于不可变对象,如整数、字符串、元组,可能是为了优化性能,python会重复使用已有的对象,即使是两个不同变量,在内容相同的情况下,其实指向的内存地址也是一样的。

2:对于可变对象,如列表、字典,即使内容相同,赋值出来的两个对象(包括列表的元素和字典的值),它们的内存地址也是不同的。

3:对于可变对象内部嵌套的不可变对象,比如列表中嵌套的列表的元素为整数时,字典中嵌套的字典的值为字符串时,它们也会重复使用已有对象的内存地址。

问题三:浅拷贝和深拷贝

含义:

浅拷贝:只复制对象的结构(顶层属性,即列表和字典,以及列表中元素和字典的值),不复制其内部的嵌套对象(比如列表中的列表,字典中的字典)。

深拷贝:复制对象的结构以及内部所有引用对象。

常见的几种复制方式:

浅拷贝:copy(),切片,copy.copy()。

深拷贝:copy.deepcopy()。

接下来看看几种拷贝的效果:

代码语言:javascript
复制
import copy
l_alpha = ['a', 'b', ['a', 'b', 'c', 'd', 'e', 'f']]
l_copy1 = l_alpha  # 引用
l_copy2 = l_alpha.copy()  # 浅拷贝
l_copy3 = l_alpha[:]  # 浅拷贝
l_copy4 = copy.copy(l_alpha)  # 浅拷贝
l_copy5 = copy.deepcopy(l_alpha)  # 深拷贝
print('原表改前 :', l_alpha)
print('列表1改前:', l_copy1)
print('列表2改前:', l_copy2)
print('列表3改前:', l_copy3)
print('列表4改前:', l_copy4)
print('列表5改前:', l_copy5)
print('——修改各个列表:')
l_alpha[0] = '0'
l_copy1[1] = '1'
l_alpha[2][0] = '0'
l_copy1[2][1] = '1'
l_copy2[2][2] = '2'
l_copy3[2][3] = '3'
l_copy4[2][4] = '4'
l_copy5[2][4] = '5'
print('原表改后:', l_alpha)
print('列表1改后:', l_copy1)
print('列表2改后:', l_copy2)
print('列表3改后:', l_copy3)
print('列表4改后:', l_copy4)
print('列表4改后:', l_copy5)

复制

输出结果为:

代码语言:javascript
复制
原表改前 : ['a', 'b', ['a', 'b', 'c', 'd', 'e', 'f']]
列表1改前: ['a', 'b', ['a', 'b', 'c', 'd', 'e', 'f']]
列表2改前: ['a', 'b', ['a', 'b', 'c', 'd', 'e', 'f']]
列表3改前: ['a', 'b', ['a', 'b', 'c', 'd', 'e', 'f']]
列表4改前: ['a', 'b', ['a', 'b', 'c', 'd', 'e', 'f']]
列表5改前: ['a', 'b', ['a', 'b', 'c', 'd', 'e', 'f']]
——修改各个列表:
原表改后: ['0', '1', ['0', '1', '2', '3', '4', 'f']]
列表1改后: ['0', '1', ['0', '1', '2', '3', '4', 'f']]
列表2改后: ['a', 'b', ['0', '1', '2', '3', '4', 'f']]
列表3改后: ['a', 'b', ['0', '1', '2', '3', '4', 'f']]
列表4改后: ['a', 'b', ['0', '1', '2', '3', '4', 'f']]
列表4改后: ['a', 'b', ['a', 'b', 'c', 'd', '5', 'f']]
2692700206272
2692700206272
2692700206400
2692700206336
2692699827392
2692700208640

可见:

表1和原表无论顶层内容还是内部嵌套的修改,都是完全同步的,因为表1不是浅拷贝也不是深拷贝,而是直接引用了原表。

表2、表3、表4和原表顶层内容的修改互不影响,对于内部嵌套的修改是互相同步的,这就是浅拷贝。

表4和原表无论顶层内容还是内部嵌套的修改,都是互不影响的,是两个完全彼此独立的对象,这就是深拷贝。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题一:不可变对象与可变对象
  • 问题二:运算符 == 与 is 的区别
  • 问题三:浅拷贝和深拷贝
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档