关于这个值传递、引用传递之类的问题 老是容易弄混淆,这次通过代码来好好梳理一遍。
>>> alist = ["lllii", 23, ["Redis", "Mysql"]]
>>> blist = alist
>>> print(id(alist))
3070901452
>>> print(id(blist))
3070901452
>>> alist[0] = "llliiiblist"
>>> alist[2].append("Pgsql")
>>> print(alist)
['llliiiblist', 23, ['Redis', 'Mysql', 'Pgsql']]
>>> print(blist)
['llliiiblist', 23, ['Redis', 'Mysql', 'Pgsql']]
>>> print(id(alist))
3070901452
>>> print(id(blist))
3070901452
>>> blist[0] = "llliiiblist-again"
>>> print(blist)
['llliiiblist-again', 23, ['Redis', 'Mysql', 'Pgsql']]
>>> print(alist)
['llliiiblist-again', 23, ['Redis', 'Mysql', 'Pgsql']]
从代码可以看出,使用=赋值的alist和blist指向同一个内存id,因此无论怎么改alist或者blist,两个都是一起变化的。也就是说,对象的赋值就是内存地址的传递。
>>> import copy
>>> alist = ["lllii", 23, ["Redis", "Mysql"]]
>>> blist = copy.copy(alist)
>>> print(id(alist))
3070854988
>>> print(id(blist))
3069998540
>>> print([id(e) for e in alist])
[3069967808, 138111136, 3070901452]
>>> print([id(e) for e in blist])
[3069967808, 138111136, 3070901452]
>>> alist[0] = "ooooohehaha"
>>> alist[2].append("Mongodb")
>>> print(alist)
['ooooohehaha', 23, ['Redis', 'Mysql', 'Mongodb']]
>>> print(blist)
['lllii', 23, ['Redis', 'Mysql', 'Mongodb']]
>>> print([id(e) for e in alist])
[3069973232, 138111136, 3070901452]
>>> print([id(e) for e in blist])
[3069967808, 138111136, 3070901452]
>>> blist[0] = "changefromblist"
>>> blist[2].append('Pgsql')
>>> print(alist)
['ooooohehaha', 23, ['Redis', 'Mysql', 'Mongodb', 'Pgsql']]
>>> print(blist)
['changefromblist', 23, ['Redis', 'Mysql', 'Mongodb', 'Pgsql']]
>>> print([id(e) for e in alist])
[3069973232, 138111136, 3070901452]
>>> print([id(e) for e in blist])
[3069973952, 138111136, 3070901452]
代码中,先使用copy模块,把alist对象浅拷贝给blist,两个对象的内存id是不一样的,说明blist是一个新的对象。
但是,对于对象中的元素,浅拷贝就只会使用原始元素的内存地址,也就是说”blist[i] is alist[i]”。
alist中第一个元素string是不可变类型,修改后该元素的内存id发生了变化;
alist中第三个元素list是可变类型,修改后该元素的内存id不变。blist同理。
使用以下操作的时候,会产生浅拷贝的效果:
1)使用切片[:]操作
2)使用工厂函数(如list/dir/set)
3)使用copy模块中的copy()函数
小结:浅拷贝产生的新对象,虽然具有完全不同的id,但是其值若包含可变对象,这些对象和原始对象中的值包含同样的引用。使用浅拷贝的典型使用场景是:对象自身发生改变的同时需要保持对象中的值完全相同,比如 list 排序。
>>> import copy
>>> alist = ["lllii", 23, ["Redis", "Mysql"]]
>>> blist = copy.deepcopy(alist)
>>> print(id(alist))
3061911756
>>> print(id(blist))
3069967756
>>> print([id(e) for e in alist])
[3069965696, 138111136, 3061907116]
>>> print([id(e) for e in blist])
[3069965696, 138111136, 3069967692]
>>> alist[1] = 25
>>> alist[2].append('Oracle')
>>> print(alist)
['lllii', 25, ['Redis', 'Mysql', 'Oracle']]
>>> print(blist)
['lllii', 23, ['Redis', 'Mysql']]
>>> print(id(alist))
3061911756
>>> print(id(blist))
3069967756
>>> print([id(e) for e in alist])
[3069965696, 138111168, 3061907116]
>>> print([id(e) for e in blist])
[3069965696, 138111136, 3069967692]
>>> blist[0] = 'changeeeee'
>>> blist[2].append('Oracleeeee')
>>> print(blist)
['changeeeee', 23, ['Redis', 'Mysql', 'Oracleeeee']]
>>> print(alist)
['lllii', 25, ['Redis', 'Mysql', 'Oracle']]
>>> print([id(e) for e in alist])
[3069965696, 138111168, 3061907116]
>>> print([id(e) for e in blist])
[3069973952, 138111136, 3069967692]
深拷贝后,blist的内存id与alist不同,但是三个元素,只有alist[2]和blist[2]的内存id不同,但是修改了alist[1]和blist[0]后,他们的内存id也改变了。原因是,对于[0],[1]两种不可变类型,是没有copy的说法的,深拷贝浅拷贝都不会改变他们的内存id。而对于[2]可变类型,深拷贝后,会创建新的对象。
小结:深拷贝不仅仅拷贝了原始对象自身,也对其包含的值进行拷贝,它会递归的查找对象中包含的其他对象的引用,来完成更深层次拷贝。因此,深拷贝产生的副本可以随意修改而不需要担心会引起原始值的改变。
(1)可变类型和不可变类型
>>> alist = [1, 4, 'hahha']
>>> id(alist)
3069967756
>>> alist.append('lueluelue')
>>> id(alist)
3069967756
>>> b = alist
>>> id(b)
3069967756
>>> num = 1
>>> id(num)
138110784
>>>
>>> num += 1
>>> id(num)
138110800
>>>
>>> num
2
>>> b = 2
>>> id(b)
138110800
>>> x = 1.5
>>> id(x)
3066236256
>>> y = 1.5
>>> id(y)
3066235424
(2)什么是工厂函数?
工厂函数:能够产生类实例的内建函数。这些内建函数都是类对象, 当调用它们时,实际上是创建了一个类实例。
(内建函数:安装好Python后就可以直接使用的函数,不需要import任何模块)
例如工厂函数list,将tuple转为list,创建了一个新的对象,很明显是浅拷贝。
>>> a = (1, 5, [2, 3])
>>> alist = list(a)
>>> print([id(e) for e in a])
[138110784, 138110848, 3071092556]
>>> print([id(e) for e in alist])
[138110784, 138110848, 3071092556]
>>> alist[2].append("haha")
>>> print([id(e) for e in alist])
[138110784, 138110848, 3071092556]
>>> id(a)
3071147420
>>> id(alist)
3071149484
参考链接:
http://wecatch.me/blog/2016/06/18/python-copy-deepcopy/
https://www.cnblogs.com/wilber2013/p/4645353.html
https://www.cnblogs.com/blackmatrix/p/5614086.html
http://www.pythontab.com/html/2018/pythonjichu_0321/1263.html
http://www.jb51.net/article/136819.htm