
含义:
不可变对象:是指创建后,内容不可修改的对象。我们只能通过重新创建这个对象的方式来实现修改它的内容。
可变对象:是指创建后,内容可修改的对象。修改内容后,这个对象本身还是之前那个对象。
常见不可变对象:如整数 int,浮点 float,字符串 str,元组 tuple 等。
常见可变对象:如列表 list,字典 dict,集合 set 等。
示例:
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))输出结果为:
修改前:
整数: 2860989612272
浮点: 2860990653264
字符串: 2860994903728
列表: 2860994720256
字典: 2860994639872
修改后:
整数: 2860989612304
浮点: 2860990652720
字符串: 2860995006832
列表: 2860994720256
字典: 2860994639872可见,修改内容后,整数、浮点数、字符串的内存地址都发生了改变,列表和字典的内存地址没变。
含义:
== 运算符:比较两个对象的值是否相等。即使两个不同对象,它们的值也可以相等。
is 运算符:比较两个对象是否相同。即它们是否指向内存的同一个位置。
用通俗的话来讲:
你家是三室一厅,你对象家也是三室一厅,就是 a = b 且 a is b,因为你俩的家指向的是同一套房子。
你家是三室一厅,某同事家也是三室一厅,就是 a = b 但是 a is not b,因为你俩的家指的不是同一套房子。
来看一些简单的示例:
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
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的内存管理机制。首先看以下代码:
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))输出结果为:
整数:
True True
2202396655856 2202396655856
字符串:
True True
2202397978480 2202397978480
元组:
True True
2202398310208 2202398310208可见,此时指向的内存地址是一样的。
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]))输出结果为:
列表:
2456080737536 2456080694528
2456080740864 2456081027072
2456080409136 2456080409136
字典:
2456080660480 2456080660736
2456081026816 2456081027712
2456080802672 2456080802672可见,列表和字典顶层内容的内存地址是不一样的,而内部嵌套的整数和字符串的内存地址却是重复的。
总结:
1:对于不可变对象,如整数、字符串、元组,可能是为了优化性能,python会重复使用已有的对象,即使是两个不同变量,在内容相同的情况下,其实指向的内存地址也是一样的。
2:对于可变对象,如列表、字典,即使内容相同,赋值出来的两个对象(包括列表的元素和字典的值),它们的内存地址也是不同的。
3:对于可变对象内部嵌套的不可变对象,比如列表中嵌套的列表的元素为整数时,字典中嵌套的字典的值为字符串时,它们也会重复使用已有对象的内存地址。
含义:
浅拷贝:只复制对象的结构(顶层属性,即列表和字典,以及列表中元素和字典的值),不复制其内部的嵌套对象(比如列表中的列表,字典中的字典)。
深拷贝:复制对象的结构以及内部所有引用对象。
常见的几种复制方式:
浅拷贝:copy(),切片,copy.copy()。
深拷贝:copy.deepcopy()。
接下来看看几种拷贝的效果:
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)复制
输出结果为:
原表改前 : ['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 删除。