为什么函数可以直接修改列表,但不能修改字符串。
这次我们来谈谈,为什么函数可以直接修改列表,但不能修改字符串。
比如这段代码,定义了一个字符串和一个列表,然后这里有个函数,会在传入的东西后面加个感叹号。将字符串和列表分别传入函数,然后再打印这两个对象,会发现字符串没有被改变,列表却被改变了。
是不是有点奇怪?如果是因为函数的作用域,而使字符串没有变,那为什么列表变了;如果是因为函数可以改变对象,那为什么字符串没有变。
那么这到底是怎么回事呢,让我们从 Python 变量是什么讲起。
变量都是引用
你应该听说过一句话,Python 中所有的变量都是引用。我们可以直接理解为变量是对象的标签。
举个例子,这里有一个对象,是一个列表 [1,2,3]
。
当我们将这个对象和一个变量绑定时,比如说绑定到 x 上时,就相当于往这个对象上贴了一个标签。
一个对象可以不止贴一个标签,我还可以写 y=x
来给这个对象再贴上标签 y。
而我们将对象传入函数这个动作,实际上就是给这个对象添加了一个形参的标签。
函数传参传引用
我这里定义了一个函数,查看传入对象的 id。这个函数的形参是 obj
,当传入一个对象进去,就会给对这个对象添加一个叫 obj
的标签。
因为一个对象只会有一个 id,我们可以通过 id 来判断是否是同一个对象。
将刚刚的字符串和列表分别传进来看看结果。
你可以看到,传入前后对象的 id 没有变化。可以理解为,进入函数时,给这个对象贴上了一个临时标签 obj
,退出函数后,这个标签就撕掉了。
这样想,当我们传入一个可变对象时,比如说列表,就很能理解为什么这个列表会被改动了,因为 obj
和 my_lst
就是一个东西。
那为什么字符串没有改变呢?
可变对象和不可变对象
这涉及到可变对象和不可变对象,字符串作为一个不可变对象是不能被修改的,当你尝试修改一个不可变对象,实际上是新定义了一个对象。也就是标签从这个对象撕下,贴到了另一个对象上。
我们来验证一下。修改这个函数的功能,让它改变传进来的 obj
,并且打印出修改前后的 id。
再运行一次。
你会看到字符串 after change 这里的 id 变了。
最后
这个的内容可能没有办法立刻用在你的代码中,但明白这个逻辑可以让你识别出很多不那么明显的问题,也可以让你更加得心应手地使用参数。