Post

为什么函数可以直接修改列表,但不能修改字符串。

这次我们来谈谈,为什么函数可以直接修改列表,但不能修改字符串。

比如这段代码,定义了一个字符串和一个列表,然后这里有个函数,会在传入的东西后面加个感叹号。将字符串和列表分别传入函数,然后再打印这两个对象,会发现字符串没有被改变,列表却被改变了。

是不是有点奇怪?如果是因为函数的作用域,而使字符串没有变,那为什么列表变了;如果是因为函数可以改变对象,那为什么字符串没有变。

那么这到底是怎么回事呢,让我们从 Python 变量是什么讲起。

变量都是引用

你应该听说过一句话,Python 中所有的变量都是引用。我们可以直接理解为变量是对象的标签。

举个例子,这里有一个对象,是一个列表 [1,2,3]

当我们将这个对象和一个变量绑定时,比如说绑定到 x 上时,就相当于往这个对象上贴了一个标签。

一个对象可以不止贴一个标签,我还可以写 y=x 来给这个对象再贴上标签 y。

而我们将对象传入函数这个动作,实际上就是给这个对象添加了一个形参的标签。

函数传参传引用

我这里定义了一个函数,查看传入对象的 id。这个函数的形参是 obj,当传入一个对象进去,就会给对这个对象添加一个叫 obj 的标签。

因为一个对象只会有一个 id,我们可以通过 id 来判断是否是同一个对象。

将刚刚的字符串和列表分别传进来看看结果。

你可以看到,传入前后对象的 id 没有变化。可以理解为,进入函数时,给这个对象贴上了一个临时标签 obj,退出函数后,这个标签就撕掉了。

这样想,当我们传入一个可变对象时,比如说列表,就很能理解为什么这个列表会被改动了,因为 objmy_lst 就是一个东西。

那为什么字符串没有改变呢?

可变对象和不可变对象

这涉及到可变对象和不可变对象,字符串作为一个不可变对象是不能被修改的,当你尝试修改一个不可变对象,实际上是新定义了一个对象。也就是标签从这个对象撕下,贴到了另一个对象上。

我们来验证一下。修改这个函数的功能,让它改变传进来的 obj ,并且打印出修改前后的 id。

再运行一次。

你会看到字符串 after change 这里的 id 变了。

最后

这个的内容可能没有办法立刻用在你的代码中,但明白这个逻辑可以让你识别出很多不那么明显的问题,也可以让你更加得心应手地使用参数。

This post is licensed under CC BY 4.0 by the author.