Python 列表、变量以及参数传递——从删除列表第一个元素说起
Python 的列表、变量以及参数传递一直都是一个挺让人头疼的问题。什么时候是值传递,什么时候是引用传递,总是傻傻搞不清。
举个例子来说,下面三段代码都希望能够删除列表第一个元素,但为什么前两个能实现功能,但第三个不行呢?
# Example 1
def delete_head(new_list):
del new_list[0]
old_list = [0, 1, 2, 3, 4]
delete_head(old_list)
print(old_list)
# Output: [1, 2, 3, 4]
# Example 2
def tail(new_list):
return new_list[1:]
old_list = [0, 1, 2, 3, 4]
old_list = tail(old_list)
print(old_list)
# Output: [1, 2, 3, 4]
# Example 3
def bad_delete_head(new_list):
new_list = new_list[1:]
old_list = [0, 1, 2, 3, 4]
bad_delete_head(old_list)
print(old_list)
# Output: [0, 1, 2, 3, 4]
为了解决这个问题,我们需要对 Python 的变量有一个基本的认识:
- Python 中的变量都是对内存中某个对象的引用。
- 一般变量(不可更改对象)是对数据对象的引用,列表、字典(可更改对象)是对一般变量(不可更改对象)的引用。
- Python 的函数参数传递总是“值传递”,或者说,传递的总是对内存中某个对象的引用关系。
基于这样的初步认识,我们再来依次分析一下上面的三段代码。
例子 1
# Example 1
def delete_head(new_list):
del new_list[0]
old_list = [0, 1, 2, 3, 4]
delete_head(old_list)
print(old_list)
# Output: [1, 2, 3, 4]
当第 5 行 old_list = [0, 1, 2, 3, 4]
这段代码执行过后,内存中是这样的:
当我们调用 delete_head
函数的时候,Python 解释器将我们传入的参数 old_list
复制了一份成为 new_list
,像这样:
这里我们再次强调,Python 始终是“值传递”;而这里的“值”,指的是引用关系。
接下来,程序执行到第 3 行 del new_list[0]
。del
表示从内存中删掉一个变量,同时删除链接到它的引用关系。我们将与 new_list[0]
指向的变量、相关的引用关系标红:
这里对于
Data 0
,没有其他的变量引用到它,因此这个数据对象也会被销毁。
当完成删除后,内存中就是这样的:
很显然,这改变了 old_list
内容,符合我们的期望。
例子 2
# Example 2
def tail(new_list):
return new_list[1:]
old_list = [0, 1, 2, 3, 4]
old_list = tail(old_list)
print(old_list)
# Output: [1, 2, 3, 4]
列表初始化、函数调用的传参和例子 1 相似,我们得到这样的内存结构:
现在,new_list[1:]
语句会复制一份新的列表,内存会变成这样:
之后,我们通过 return
,将这个列表赋值给了 old_list
,如图:
于是,这个程序成功地改变了 old_list
内容,符合我们的期望。
例子 3
# Example 3
def bad_delete_head(new_list):
new_list = new_list[1:]
old_list = [0, 1, 2, 3, 4]
bad_delete_head(old_list)
print(old_list)
# Output: [0, 1, 2, 3, 4]
对于这个程序,直到 new_list[1:]
都与例子 2 一模一样。在 new_list[1:]
复制一份新的列表之后:
接下来,程序将复制后的列表赋值给了 new_list
,然后函数结束,new_list
被释放:
在这个过程中,old_list
是始终没有被修改的,使得最后结果是错误的。
后记
今天一个同学在 QQ 上问我了这样的一个问题,于是我就跑去查了些材料、画了些图,然后给他作了解答。想着好不容易画的图不能这么轻易浪费,于是将回答整理了一下,形成了这篇文章。
当然,这篇文章只提到了列表的参数传递,并没有提到普通变量的传递,而且我也不确定内容是否完全正确……如果有什么错误或者不太恰当的地方,还请指出。
$\empty$
参考资料
本作品采用知识共享署名 4.0 国际许可协议进行许可。
本文链接:https://blog.ceba.tech/2020/12/Summary-of-Python-List-and-Var/