文章目录
一、变量、可变对象与不可变对象
变量指向一个对象,而对象有可变与不可变之分
>>> 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
务必搞清楚,改变的是对象本身,还是仅得到了一个中间结果
注意:
- 变量无类型,对象有类型
- 对象是内存中存储数据的实体,变量则是指向对象的指针(当指向的对象可变时,可以用这个变量来修改对象本身;指向的对象不可变时,修改变量会让变量指向一个新对象)
*总结:
- 不可变对象:对象所指向的内存中的值不能被改变,当改变这个变量的时候,原来指向的内存中的值不变,变量不再指向原来的值,而是开辟一块新的内存,变量指向新的内存。
- 可变对象:对象指向的内存中的值会改变,当更改这个变量的时候,还是指向原来内存中的值,并且在原来的内存值进行原地修改,并没有开辟新的内存。
- 在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()函数