最近用python做题,因为对python的机制了解甚浅而踩了不少坑,关于方法中修改变量值的问题,吃亏很多次后终于懂了一丢丢,应该属于入门级的低端错误555555~仅记录一些自己不成熟的理解。
之前和同学开玩笑的时候说,python里面没有指针这个名词了,因为全都是指针。在调用方法的时候也要记得,传递的参数实际上是指向一个已经存在的对象,因此在子方法中操作变量时,也要根据它“指向的对象”来判断修改情况。
比如一个非常简单的例子:
def add(a):
a += 1
a = 1
add(a)
a
Out[43]: 1
这个例子中的int值a可以换成字符串类型,会得到相同的结果,即无法在子方法外改变变量的值,要获取这个值我们就只能通过子方法的返回值,用一个变量来接住这个值(或者用全局变量)。为什么会这样呢,我们来看int型变量a在自增1之前的id变化:
a = 1
id(a)
Out[52]: 1690510000
a += 1
id(a)
Out[54]: 1690510032
当a被自增时,它的id是变化的,也就是说,在子方法中我们实际上是用“a”这个名字覆盖了另一个值,这个值是原来a自增1的结果,但是子方法外的那个“a”,它的值变化了吗?并没有。有点类似于局部变量的意思。
字符串同理。
对于列表,它本身是可以进行原地修改的,我们就会看到:
def add(a):
a[0] = 0
a = [1,2,3]
add(a)
a
Out[62]: [0, 2, 3]
显而易见地,我们有:
a = [1,2,3]
id(a)
Out[64]: 2435253695048
a[0] = 0
id(a)
Out[66]: 2435253695048
在子方法中,对列表进行任意形式切片操作或append、extend、insert,都是可以不借助返回值直接修改变量元素的,这其中的原因就是其“原地操作”的性质。而字符串是不可变数据类型,当我们写下a = "abc" a += "a" 这样的句子时,实际上是用一个新的地址覆盖了原来的。
对于列表,还有一个点,就是 += 这个操作。我们需要区别a = a + b 和 a += b。
a = a + b 式操作:
a = [1,2,3]
id(a)
Out[78]: 2435253692936
a = a + [0]
a
Out[80]: [1, 2, 3, 0]
id(a)
Out[81]: 2435252828808
a += b 式操作:
a = [1,2,3]
id(a)
Out[83]: 2435253690888
a += [0]
a
Out[85]: [1, 2, 3, 0]
id(a)
Out[86]: 2435253690888
容易看出,对于list(也许还有别的什么数据结构),+=是一个原地操作运算符,就是说在方法中可以通过+=修改列表元素。
最后皮一下:
def add(a):
a = a + [0]
a += [0]
a = [1,2,3]
add(a)
a
Out[90]: [1, 2, 3]
a的值是不变的,这一点虽然现在看起来显而易见,但是第一次我写出这种函数的时候真是百思不得其解,+=不是可以原地操作的吗?为什么我一直改不了数据?(ˉ▽ˉ;)...当时还调了大半天。