Python中的赋值、浅拷贝和深拷贝的区别

一、变量、可变对象与不可变对象

变量指向一个对象,而对象有可变与不可变之分

>>> a = "HELLO"
>>> id(a)
140182492168632
>>> a = "hello"
>>> id(a)
140182492168688
>>> a = ["hello_1", "hello_2"]
>>> id(a)
140182492190088
>>> a.append("hello_3")
>>> id(a)   #当可变对象的内容改变时,指向它的变量保存的地址不变
140182492190088

可变对象与不可变对象

>>> L = ['hello_2', 'hello_3', 'hello_1']
>>> print(sorted(L))    #sorted()函数不改变传入的参数本身
['hello_1', 'hello_2', 'hello_3']
>>> print(L)
['hello_2', 'hello_3', 'hello_1']
>>> L.sort()    #List的成员函数sort()会改变自己
>>> print(L)
['hello_1', 'hello_2', 'hello_3']

>>> s = 'hello'
>>> print(s.replace('h', 'H'))  #因为str是不可变数据类型,所以会返回一个新字符串
Hello
>>> print(s)
hello

务必搞清楚,改变的是对象本身,还是仅得到了一个中间结果

注意:

  • 变量无类型,对象有类型
  • 对象是内存中存储数据的实体,变量则是指向对象的指针(当指向的对象可变时,可以用这个变量来修改对象本身;指向的对象不可变时,修改变量会让变量指向一个新对象)

*总结:

  1. 不可变对象:对象所指向的内存中的值不能被改变,当改变这个变量的时候,原来指向的内存中的值不变,变量不再指向原来的值,而是开辟一块新的内存,变量指向新的内存。
  2. 可变对象:对象指向的内存中的值会改变,当更改这个变量的时候,还是指向原来内存中的值,并且在原来的内存值进行原地修改,并没有开辟新的内存。
  3. 在Python中,数值类型int、float、字符串str、元祖tuple、bool都是不可变对象;列表list、集合set、字典dict都是可变对象

二、赋值、浅拷贝与深拷贝

2.1 不可变对象的赋值、浅拷贝和深拷贝

import copy
a=(1,2,3)

print("=====赋值=====")
b=a
print(a)
print(b)
print(id(a))
print(id(b))

print("=====浅拷贝=====")
b=copy.copy(a)
print(a)
print(b)
print(id(a))
print(id(b))

print("=====深拷贝=====")
b=copy.deepcopy(a)
print(a)
print(b)
print(id(a))
print(id(b))

结果:

=====赋值=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
=====浅拷贝=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
=====深拷贝=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128

因此可以看到,对于不可变对象的无论是赋值、浅拷贝还是深拷贝,都没有创建新的对象空间,而是指向的原对象地址。

2.2 可变对象的赋值、浅拷贝和深拷贝

2.2.1 赋值

可变类型对象赋值传递的是引用,类似于C语言中的指针

>>> a
['hello_1', 'hello_2', 'hello_3']
>>> id(a)
140182492190088
>>> b = a   #对于可变对象变量的赋值,改变其中一个变量对象的内容,都会影响另一个变量对象,因为他们指向的是同一个对象
>>> id(b)
140182492190088
>>> a.append("hello_4")
>>> a[0] = "hello_5"
>>> a
['hello_5', 'hello_2', 'hello_3', 'hello_4']
>>> b
['hello_5', 'hello_2', 'hello_3', 'hello_4']
>>> id(a) == id(b)
True

2.2.2 浅拷贝

浅拷贝有三种形式: 切片操作,工厂函数,copy模块中的copy函数。
浅拷贝只拷贝父对象,不会拷贝对象的内部的子对象

import copy

lst = [1,2,[3,4]]

# 切片操作
lst1 = lst[:]
lst1 = [each for each in lst]

# 工厂函数
lst1 = list(lst)

# copy函数
lst1 = copy.copy(lst)

2.2.3 深拷贝

深拷贝完全拷贝了父对象及其子对象,具体通过copy 模块的 deepcopy 方法实现

>>>import copy
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})

2.2.4 对比总结

import copy

a = [[1, 2, 3], [4, 5, 6]]
b = a   #此处的b类似于别名
c = copy.copy(a)
d = copy.deepcopy(a)

a.append(7)
a[1][2] = 10

print('原数组: ', a)
print('引用赋值: ', b)
print('浅拷贝: ', c)
print('深拷贝: ', d)

结果为:

原数组: [[1, 2, 3], [4, 5, 10], 7]
引用赋值: [[1, 2, 3], [4, 5, 10], 7]
浅拷贝: [[1, 2, 3], [4, 5, 10]]
深拷贝: [[1, 2, 3], [4, 5, 6]] 

总结

  • 赋值就是创建了对象的一个新的引用,修改其中任意一个变量都会影响到另一个。
  • 浅拷贝是创建一个新的对象,但它包含的是对原始对象中所有元素项的引用(如果用引用的方式修改其中一个对象,另外一个也会修改改变):
    • 完全切片方法;
    • 工厂函数,如list();
    • copy模块的copy()函数}。
  • 深拷贝创建一个新的对象,并且递归的复制它所包含的对象(修改其中一个,另外一个不会改变):
    • copy模块的deep.deepcopy()函数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值