刷lc题的时候,老踩这种坑,索性搞懂。
先有概念:
1、python里面的所有元素都是对象,实例化一个对象就会开辟新的内存空间,然后返回一个引用给你。
2、a = 3。这里实例化了一个对象3,用变量名a表示。需要明确a不是对象本身,a只是这个对象3的引用。(回忆一下c++里面的指针和引用)
可变对象和不可变对象
可变对象和不可变对象的本质区别就是:对象本身的值可不可变。
可变对象:可变对象的值是可以原地 (inplace) 改变的,我们可以通过引用 (变量名) 来操作可变对象,使得它的值原地变化(即不开辟新的内存空间,地址不变)。
包括:list、set、dict(添加值我们不把它看成开辟“新”的内存空间,因为地址没变)。
不可变对象:不可变对象的值是不能改变的。如果引用 (变量名) 如果想要改变值,那不好意思,请你找新的对象去吧!(也就是会实例化新的对象,开辟新的内存空间,产生一个新的地址)
包括:int、str、float、tuple
Demo(此图出现太多,不明出处):不可变对象 int可变对象 list
+= 和 =+
这两个操作符,和可变/不可变对象有密切联系。
+=:调用对象的__iadd__方法,若不存在,才调用__add__方法;
=+:直接调用对象的__add__方法。
__iadd__方法直接在原地 (inplace) 修改对象的值,返回值为None,因此引用的地址不变;而__add__方法会返回一个新的对象,原对象不变。
和可变/不可变对象的联系:
一般情况下,可变对象有__iadd__方法,而不可变对象只有__add__方法。
另:对list而言,+= 和 extend() 等价。
Demo:
坑
坑2:dp初始化二维数组时,下面注释里面那种写法是错的。
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
LA, LB = len(text1), len(text2)
# dp = [[0]*(LB+1)]*(LA+1) ##########这里!!
dp = [[0]*(LB+1) for _ in range(LA+1)]
for i in range(LA-1,-1,-1):
for j in range(LB-1,-1,-1):
if text1[i] == text2[j]:
dp[i][j] = dp[i+1][j+1] + 1
else:
dp[i][j] = max(dp[i][j+1], dp[i+1][j])
return dp[0][0]
为什么呢?因为int是不可变对象,复制出来都是新的对象,所以第一维没有问题。而list复制的只是引用,第二维看似复制了很多次,实际上都指向同一个对象。
所以可以用*复制不可变对象,但千万不要用来复制可变对象!错误(左),正确(右)
深Copy和浅Copy
这篇文章写得很好,就不重复造轮子了。张小鸡:5张图彻底理解Python中的浅拷贝与深拷贝zhuanlan.zhihu.com
总结一下:
1、赋值
赋值操作,其实就是给一段内存地址贴标签。
对于不可变对象,=、copy、deepcopy三者等价。
2、浅拷贝
拷贝父对象的副本,但不会拷贝对象的内部的子对象。
或者说浅拷贝只是拷贝了原始元素的引用(内存地址),拷贝对象的值是否会随着被拷贝对象变化,仅取决于原始元素是可变对象还是不可变对象。
3、深拷贝
完全拷贝了父对象及其子对象。
深拷贝复制的不只是原始元素的引用,而是整个原始元素对象。因此,拷贝对象和被拷贝对象的值相互独立。
参考: