Python函数传参本质:从内存地址看值传递与引用传递
本文深入解析Python函数参数传递机制,指出简单区分值传递或引用传递并不准确。通过id()查看内存地址,揭示不可变对象共享地址、可变对象创建即分配新地址的特性。文章通过代码实验说明,函数内对可变对象的修改(如pop)与重新赋值(如+)对原对象的影响不同,核心在于Python动态语言特性下的内存分配策略。
目前网络上许多技术博客对此问题的结论往往如下:
Python并未允许程序员在传值与传引用之间进行选择,其参数传递机制实质上采用的是“传对象引用”。然而,这种机制并非单纯的传引用,而是传值与传引用的综合体现。当函数接收的是可变对象(如字典或列表)的引用时,可以直接修改该对象的原始内容,这类似于“传引用”的效果;而当接收的是不可变对象(如整数、字符串或元组)的引用时,则无法直接修改原始对象,这类似于“传值”的效果。
上述观点在大量探讨该话题的博客中广泛流传。
然而,在实际编码操作中,我发现了一个值得注意的现象:
l=[1,2,3] def a(x): x=x+[4] a(l) print(l)
执行上述代码后,输出结果为:
[1,2,3]
为何会出现这种情况?既然list是可变对象,按照前述结论,传递方式应为引用传递,我理应在函数内部修改它。难道预期输出不应该是[1, 2, 3, 4]吗?
我认为,之前引用的那些主流博主的结论虽然常见,但极易引起误解,且未触及本质,读起来令人困惑。
经过后续多次实验验证,我得出以下结论:
在Python中,单纯讨论“值传递”还是“引用传递”并无太大意义。要真正解释这些现象,关键在于理解Python在赋值过程中(针对可变与不可变对象)是如何分配内存地址的。
因此,我们将暂时搁置值传递与引用传递的争论。
让我们进行一个简单的小实验,利用id()函数查看变量在内存中的地址:
l1=[1,2,3] l2=[1,2,3] a=1 b=1 print(id(l1)) print(id(l2)) print(id(a)) print(id(b))
在我的电脑环境中,运行结果如下:
12856594504 12856915080 1643643344 1643643344
由此可见,对于可变对象list而言,即使两个列表的内容完全相同,Python也会为它们分配不同且独立的内存地址。
相反,对于不可变对象int而言,内存中只存在一个值为1的对象。即使定义另一个变量c=1,它也会指向内存中同一个值为1的对象。换言之,不可变对象的地址是共享的。
接下来,让我们观察在函数中调用并修改可变对象与不可变对象时的情形。
首先看不可变对象int的最简单情况:
a=1 print(id(a)) def x(a): print(id(a)) b=a print(id(b)) x(a)
运行结果如下:
1643643344 1643643344 1643643344
这看起来像是引用传递,函数外部的a、函数内部的a和b都指向了相同的内存地址。
但我们再考察一个极端情况:
a=1 print(id(a)) def x(): b=1 print(id(b)) x()
运行结果如下:
1643643344 1643643344
这是否令人惊讶?函数外部定义的a和函数内部定义的b看似毫无关联,但它们却指向了相同的内存地址!
那么,如何判断这究竟是值传递还是引用传递?讨论这个问题实际上没有意义,因为内存中只有一个值为1的对象。当我将值1传递给函数中的某个变量时,我实际上也传递了该地址,因为内存中仅存在这一个1。
甚至,我可以直接在函数内部给b赋值为1,这同样会使函数外部的a和函数内部的b指向同一个内存地址。
下面来看看传递可变对象list的情况:
l=[1,2,3] print(id(l)) def a(x): print(id(x)) x.pop() print(x) print(id(x)) x=x+[3] print(x) print(id(x)) a(l)
运行结果如下:
883142451528 [1, 2] [1, 2, 3]
可以看到,当我们将函数外部的列表L传递给函数后,变量x的地址与L一致,这看起来确实是引用传递,没有问题。
继续执行,当我们调用x自身的方法pop后,x变为[1, 2],且x的内存地址保持不变,这也在预期之中。
但是,当我们对x进行赋值操作后,x的内存地址发生了改变。
也就是说,只要创建了一个新的可变对象,Python就会分配一个新的内存地址。即使我们创建的新可变对象与已存在的旧可变对象内容完全一致,Python依然会分配一个新的地址(参见本文上半部分的“简单小实验”)。
而pop方法并非创建新的可变对象,它是对已有的可变对象进行修改。
因此可以总结为:
在Python中,不可变对象是共享地址的,而创建可变对象永远会分配新的内存地址。
此时再回头思考值传递和引用传递的问题,就会发现,在Python中讨论这一概念确实意义不大。
我们可以说:Python拥有一套独特的传参方式,这是由其作为动态语言的特性所决定的。
总结
以上就是本文关于深入理解Python中函数传递参数是值传递还是引用传递的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:Python实现一个简单的验证码程序、Python编程django实现同一个ip十分钟内只能注册一次、简单了解Python中的几种函数等,有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









