@[马克飞象]
python 中变量名和对象是分离的
例子 1:
a = 1
这是一个简单的赋值语句, 整数 1 为一个对象, a 是一个引用, 利用赋值语句, 引用 a 指向了对象 1.
例子 2:>>> a = 1>>> id(a)
- 24834392>>> a = 'banana'>>> id(a)
- 139990659655312
第一个语句中, 2 是储存在内存中的一个整数对象, 通过赋值 引用 a 指向了 对象 1
第二个语句中, 内存中建立了一个字符串对象 banana, 通过赋值 将 引用 a 指向了 banana, 同时, 对象 1 不在有引用指向它, 它会被 python 的内存处理机制给当我垃圾回收, 释放内存
例子 3:>>> a = 3>>> b = 3>>> id(a)
- 10289448>>> id(b)
- 10289448
在这里可以看到 这俩个引用 指向了同一个 对象, 这是为什么呢? 这个跟 python 的内存机制有关系, 因为对于语言来说, 频繁的进行对象的销毁和建立, 特别浪费性能所以在 Python 中, 整数和短小的字符, Python 都会缓存这些对象, 以便重复使用
例子 4:>>> a = 4>>> b = a>>> id(a)
- 36151568>>> id(b)
- 36151568>>> a = a+2>>> id(a)
- 36151520>>> id(b)
- 36151568
可以看到 a 的引用改变了, 但是 b 的引用未发生改变; a,b 指向不同的对象; 第 3 句对 a 进行了重新赋值, 让它指向了新的 对象 6; 即使是多个引用指向同一个对象, 如果一个引用值发生变化, 那么实际上是让这个引用指向一个新的引用, 并不影响其他的引用的指向从效果上看, 就是各个引用各自独立, 互不影响
例子 5:>>> L1 = [1,2,3]>>> L2 = L1>>> id(L1)
- 139643051219496>>> id(L2)
- 139643051219496>>> L1[0] = 10>>> id(L1)
- 139643051219496>>> id(L2)
- 139643051219496>>> L2
- [10, 2, 3]
同样的跟第四个例子那样, 修改了其中一个对象的值, 但是可以发现 结果 并不与 第四个栗子那样, 在本次实验中, L1 和 L2 的引用没有发生任何变化, 但是 列表对象[1,2,3] 的值 变成了 [10,2,3](列表对象改变了)
在该情况下, 我们不再对 L1 这一引用赋值, 而是对 L1 所指向的表的元素赋值结果是, L2 也同时发生变化
原因何在呢? 因为 L1,L2 的指向没有发生变化, 依然指向那个表表实际上是包含了多个引用的对象(每个引用是一个元素, 比如 L1[0],L1[1]..., 每个引用指向一个对象, 比如 1,2,3), 而 L1[0] = 10 这一赋值操作, 并不是改变 L1 的指向, 而是对 L1[0], 也就是表对象的一部份(一个元素), 进行操作, 所以所有指向该对象的引用都受到影响
(与之形成对比的是, 我们之前的赋值操作都没有对对象自身发生作用, 只是改变引用指向)
列表可以通过引用其元素, 改变对象自身 (in-place change) 这种对象类型, 称为可变数据对象(mutable object), 词典也是这样的数据类型
而像之前的数字和字符串, 不能改变对象本身, 只能改变引用的指向, 称为不可变数据对象(immutable object)
我们之前学的元组(tuple), 尽管可以调用引用元素, 但不可以赋值, 因此不能改变对象自身, 所以也算是 immutable object.
例子 6:
- l = [1,2,3]
- for item in l:
- item = 8
- # 这里只是让 item 指向 l[i]所指向的对象, item = 8, 则 item 指向新的对象 8, 不改变 l[i]
- print(l) # [1,2,3]
- for i in range(len(l)):
- l[i] = 8
- # 这里令 l[i]指向新的对象 8
- print(l) # [8,8,8]
例子 7:
- l1 = []
- a = 0
- for i in range(1,5):
- a = i
- l1.append(a) # 添加的是 a 指向的对象
- print(l1) # [1, 2, 3, 4]
- l2 = []
- b = [1,2,3]
- for i in range(1,5):
- b[1] = i
- l2.append(b) # 添加的是 b 指向的对象, 它包括列表元素的引用, 列表本身没有改变, 只是列表项 [1] 指向的对象变了
- print(l2) # [[1, 4, 3], [1, 4, 3], [1, 4, 3], [1, 4, 3]]
- # 不是预料的 [[1, 1, 3], [1, 2, 3], [1, 3, 3], [1, 4, 3]]
可以参考例子 5 所以, 每次列表实际上都是添加同一个对象
- l2 = []
- b = [1,2,3]
- for i in range(1,5):
- b[1] = i
- l2.append(copy.copy(b))
- # l2.append(copy.deepcopy(b)) 和 copy.copy()结果一样
- print(l2) # [[1, 1, 3], [1, 2, 3], [1, 3, 3], [1, 4, 3]]
copy.copy() 浅拷贝只拷贝父对象, 不会拷贝对象的内部的子对象
那么, copy.copy()和 copy.deepcopy()有什么区别呢?
- l2 = []
- b = [1,[4,5],3]
- for i in range(1,5):
- b[1][0] = i
- l2.append(copy.copy(b)) # [[1, [4, 5], 3], [1, [4, 5], 3], [1, [4, 5], 3], [1, [4, 5], 3]]
- # l2.append(copy.deepcopy(b)) # [[1, [1, 5], 3], [1, [2, 5], 3], [1, [3, 5], 3], [1, [4, 5], 3]]
- print(l2)
copy.deepcopy() 深拷贝 拷贝对象及其子对象
例子 8:
函数的参数传递探究
- def testPara(aList):
- aList[0] = 8
- print("inside function",aList) # [8, 2, 3]
- if __name__ == '__main__':
- l1 = [1,2,3]
- testPara(l1)
- print(l1) # [8, 2, 3]
- def testPara(aList):
- aList = [1,2]
- print("inside function",aList) # [1,2] aList 重新指向一个新的对象
- if __name__ == '__main__':
- l1 = [1,2,3]
- testPara(l1)
- print(l1) # [1, 2, 3]
这里要想直接把 l1 在函数内改变, 可以在函数 return l1
- def test(l:list):
- l.pop(0)
- if __name__ == '__main__':
- l = [1,2,3]
- test(l)
- print(l) # [2,3]
来源: https://www.cnblogs.com/howe670/p/8600851.html